FINAL suepr merge step : added all modules to this super repos
This commit is contained in:
339
sites/all/modules/contrib/editor/better_formats/LICENSE.txt
Normal file
339
sites/all/modules/contrib/editor/better_formats/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
30
sites/all/modules/contrib/editor/better_formats/README.txt
Normal file
30
sites/all/modules/contrib/editor/better_formats/README.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
This very basic documentation for during development.
|
||||
Better docs will be generated closer to a full release.
|
||||
|
||||
|
||||
The only items currently implented in the D7 version of Better Formats are:
|
||||
|
||||
1. Display options: When BF is enabled you will have permissions at
|
||||
admin/people/permissions to control per role display of:
|
||||
1. format tips
|
||||
2. format tips link
|
||||
3. format selection for [entity]
|
||||
|
||||
#3 is actually several permissions. There is one for each entity in your site.
|
||||
|
||||
2. Simple field level default format.
|
||||
This allows you set a field level default format using the standard "Default Value"
|
||||
setting of a field. This is only possibly normally if you enter something in the
|
||||
text field for the field api to save the format too. BF gives you the ability
|
||||
to set the format WITHOUT having to set a value in the field.
|
||||
|
||||
1. At admin/config/content/formats/settings enable "Use field default" option.
|
||||
2. Create a text type of field on one of your content types.
|
||||
3. Ensure you set the "Text processing" option to "Filtered text".
|
||||
4. Save the field.
|
||||
5. Now go back and edit the field you just saved. This is required because of
|
||||
how the field default value option works.
|
||||
6. You will now see a "Text format" dropdown below your field in the
|
||||
"Default Value" area. Set the default format in the dropdown.
|
||||
7. Save the field. Default will now be used on all new content forms for that field.
|
||||
|
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains FAPI and theme functions for the format defaults form.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Builds the form for the filters admin.
|
||||
*
|
||||
* @return
|
||||
* FAPI array
|
||||
*
|
||||
* @see better_formats_defaults_admin_form_validate()
|
||||
* @see better_formats_defaults_admin_form_submit()
|
||||
*/
|
||||
function better_formats_defaults_admin_form($form, &$form_state) {
|
||||
// Ensure all roles have a BF default entries in the database.
|
||||
better_formats_check_roles();
|
||||
|
||||
// Build defaults form.
|
||||
$form = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$nform = better_formats_get_role_default_fields('node');
|
||||
$cform = better_formats_get_role_default_fields('comment');
|
||||
$bform = better_formats_get_role_default_fields('block');
|
||||
$form = array_merge($form, $nform, $cform, $bform);
|
||||
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save defaults'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates better_formats_admin_filter_form.
|
||||
*
|
||||
* @see better_formats_defaults_admin_form()
|
||||
* @see better_formats_defaults_admin_form_submit()
|
||||
*/
|
||||
function better_formats_defaults_admin_form_validate($form, &$form_state) {
|
||||
$formats = filter_formats();
|
||||
foreach ($formats as $fid => $format) {
|
||||
$roles[$fid] = explode(',', $format->roles);
|
||||
}
|
||||
// Get roles that have administer filters permission.
|
||||
$admin_roles = better_formats_get_roles_by_perm('administer filters');
|
||||
|
||||
foreach ($form_state['values'] as $key => $values) {
|
||||
if (strpos($key, 'node-') === 0 || strpos($key, 'comment-') === 0 || strpos($key, 'block-') === 0) {
|
||||
list($type, $rid) = explode('-', $key);
|
||||
if (in_array($rid, $admin_roles)) {
|
||||
// Role has the 'administer filters' permission so it can use all formats.
|
||||
continue;
|
||||
}
|
||||
$fid = $values['format'];
|
||||
$site_default = filter_resolve_format(FILTER_FORMAT_DEFAULT);
|
||||
if ($fid != 0 && !in_array($rid, $roles[$fid]) && $fid !== $site_default) {
|
||||
form_set_error($key, t('Role does not have access to selected format.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates database from better_formats_admin_filter_form.
|
||||
*
|
||||
* @see better_formats_defaults_admin_form()
|
||||
* @see better_formats_defaults_admin_form_validate()
|
||||
*/
|
||||
function better_formats_defaults_admin_form_submit($form, &$form_state) {
|
||||
// Update DB.
|
||||
$sql = "UPDATE {better_formats_defaults}
|
||||
SET format=%d, weight=%d
|
||||
WHERE rid=%d AND type='%s'";
|
||||
|
||||
foreach ($form_state['values'] as $key => $values) {
|
||||
if (strpos($key, 'node-') === 0 || strpos($key, 'comment-') === 0 || strpos($key, 'block-') === 0) {
|
||||
list($type, $rid) = explode('-', $key);
|
||||
db_query($sql, $values['format'], $values['weight'], $rid, $type);
|
||||
}
|
||||
}
|
||||
|
||||
drupal_set_message(t('Defaults have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds FAPI form elements for the default format selection.
|
||||
*
|
||||
* @param $mode
|
||||
* 'node', 'comment', or 'block'. Top most level type for requested default.
|
||||
* @param $node_type
|
||||
* Type of node this request is for.
|
||||
* @return
|
||||
* FAPI array for the default select field.
|
||||
*/
|
||||
function better_formats_get_role_default_fields($mode, $node_type = '') {
|
||||
$form = array();
|
||||
$format_options = better_formats_get_formats_per_role();
|
||||
$type = $types = $mode;
|
||||
$per_node_type = variable_get('better_formats_per_node_type', FALSE);
|
||||
|
||||
if ($per_node_type && $node_type) {
|
||||
$type = $mode . '/' . $node_type;
|
||||
$types = $type . "','" . $mode;
|
||||
}
|
||||
|
||||
// get data from db
|
||||
$sql = "SELECT bf.*, role.name
|
||||
FROM {better_formats_defaults} AS bf
|
||||
INNER JOIN {role} AS role
|
||||
ON bf.rid = role.rid
|
||||
WHERE bf.type IN ('$types')
|
||||
ORDER BY bf.type_weight DESC, bf.weight, role.rid";
|
||||
$result = db_query($sql);
|
||||
|
||||
$roles_set = array();
|
||||
|
||||
while ($role = db_fetch_object($result)) {
|
||||
if (in_array($role->rid, $roles_set)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$roles_set[] = $role->rid;
|
||||
$key = $mode . '-' . $role->rid;
|
||||
|
||||
$form[$key]['role'] = array(
|
||||
'#value' => $role->name,
|
||||
);
|
||||
$form[$key]['format'] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => $format_options[$role->rid],
|
||||
'#default_value' => $role->format,
|
||||
'#attributes' => array('class' => 'bf-default-formats'),
|
||||
);
|
||||
$form[$key]['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#delta' => 25,
|
||||
'#default_value' => $role->weight,
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the formats available to users by role.
|
||||
*
|
||||
* Gets all formats then creates an array keyed by role IDs
|
||||
* that lists the formats available to that role. This is determined
|
||||
* by Drupal core's format permissions set at
|
||||
* admin/settings/filters/[filter_id].
|
||||
*
|
||||
* @return
|
||||
* Multi-dim array with role IDs for keys and list of allowed formats.
|
||||
*
|
||||
* @see better_formats_get_role_default_fields()
|
||||
*/
|
||||
function better_formats_get_formats_per_role() {
|
||||
$formats = filter_formats();
|
||||
$roles = user_roles();
|
||||
|
||||
// Get roles that have administer filters permission.
|
||||
$admin_roles = better_formats_get_roles_by_perm('administer filters');
|
||||
|
||||
$site_default_format = filter_resolve_format(FILTER_FORMAT_DEFAULT);
|
||||
|
||||
foreach ($formats as $format) {
|
||||
$roles_allowed = $format->roles ? explode(',', trim($format->roles, ',')) : array();
|
||||
foreach ($roles as $rid => $role) {
|
||||
$format_options[$rid][0] = t('Site default');
|
||||
if ($format->format == $site_default_format || in_array($rid, $admin_roles) || in_array($rid, $roles_allowed)) {
|
||||
$format_options[$rid][$format->format] = $format->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $format_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of roles that have a permission.
|
||||
*
|
||||
* @param $perm
|
||||
* Permission string to get roles for.
|
||||
* @param $reset
|
||||
* Boolean to clear static cache.
|
||||
* @return
|
||||
* An array of role IDs that have the requested permission.
|
||||
*/
|
||||
function better_formats_get_roles_by_perm($perm, $reset = FALSE) {
|
||||
static $roles;
|
||||
if ($reset || !isset($roles[$perm])) {
|
||||
$sql = "SELECT rid
|
||||
FROM {permission}
|
||||
WHERE perm LIKE '%$perm%'
|
||||
ORDER BY rid";
|
||||
$result = db_query($sql);
|
||||
$roles[$perm] = array();
|
||||
while ($row = db_fetch_object($result)) {
|
||||
$roles[$perm][] = $row->rid;
|
||||
}
|
||||
}
|
||||
return $roles[$perm];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process variables for better-defaults-admin-form.tpl.php.
|
||||
*
|
||||
* @param $vars
|
||||
* The $variables array contains the following arguments:
|
||||
* - $form
|
||||
*/
|
||||
function template_preprocess_better_formats_defaults_admin_form(&$vars) {
|
||||
foreach (element_children($vars['form']) as $key) {
|
||||
$form_row = &$vars['form'][$key];
|
||||
|
||||
//$type = strpos($key, 'node-') === 0 ? 'node' : 'comment';
|
||||
$type = substr($key, 0, strpos($key, '-'));
|
||||
|
||||
if (isset($form_row['role'])) {
|
||||
// Set special classes needed for table drag and drop.
|
||||
$form_row['weight']['#attributes']['class'] = 'better-formats-role-' . $type . '-weight';
|
||||
|
||||
$row = new stdClass();
|
||||
$row->role = drupal_render($form_row['role']);
|
||||
$row->format_select = drupal_render($form_row['format']);
|
||||
$row->weight_select = drupal_render($form_row['weight']);
|
||||
|
||||
$vars[$type . '_default_rows'][$key] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$vars['form_submit'] = drupal_render($vars['form']);
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains FAPI and theme functions for the format settings form.
|
||||
*/
|
||||
|
||||
/**
|
||||
* FAPI form builder for admin/settings/filters/settings page.
|
||||
*
|
||||
* @see better_formats_menu()
|
||||
*/
|
||||
function better_formats_admin_settings_form($form, &$form_state) {
|
||||
$form = array();
|
||||
|
||||
$form['control'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Control'),
|
||||
);
|
||||
$form['control']['better_formats_per_field_core'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Use field default'),
|
||||
'#description' => t('Use the core field module default value to set the default format. This will force the default format even when the default field value is empty. To set a default format you must re-edit a text field after saving it with the "Filtered text" option turned on.'),
|
||||
'#default_value' => variable_get('better_formats_per_field_core', 0),
|
||||
);
|
||||
/*
|
||||
$form['control']['better_formats_per_node_type'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Control formats per node type'),
|
||||
'#description' => t('Control formats allowed and default formats per node type. Global settings will be used until a content type admin page is saved.'),
|
||||
'#default_value' => variable_get('better_formats_per_node_type', 0),
|
||||
);
|
||||
*/
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
name = Better Formats
|
||||
description = Enhances the core input format system by managing input format defaults and settings.
|
||||
core = 7.x
|
||||
configure = admin/config/content/formats
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-04
|
||||
version = "7.x-1.0-beta1+2-dev"
|
||||
core = "7.x"
|
||||
project = "better_formats"
|
||||
datestamp = "1354624555"
|
||||
|
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Installs the better_formats module.
|
||||
*
|
||||
* Creates a database for use of multi-layered default formats and sets
|
||||
* default settings.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements of hook_install().
|
||||
*/
|
||||
function better_formats_install() {
|
||||
// Increase module weight to prevent compatibility issues.
|
||||
db_update('system')
|
||||
->fields(array('weight' => 100))
|
||||
->condition('name', 'better_formats')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements of hook_uninstall().
|
||||
*/
|
||||
function better_formats_uninstall() {
|
||||
// Delete settings from varible table.
|
||||
db_delete('variable')
|
||||
->condition('name', 'better_formats%', 'LIKE')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update from 6.x-1.2 to 7.x-1.0.
|
||||
*/
|
||||
/*
|
||||
function better_formats_update_7000() {
|
||||
|
||||
}
|
||||
*/
|
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Enhances Drupal's core text format settings.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements of hook_perm().
|
||||
*/
|
||||
function better_formats_permission() {
|
||||
$entities = entity_get_info();
|
||||
|
||||
$perms = array(
|
||||
'show format tips' => array(
|
||||
'title' => t('Show format tips'),
|
||||
'description' => t('Toggle display of format description help.'),
|
||||
),
|
||||
'show more format tips link' => array(
|
||||
'title' => t('Show more format tips link'),
|
||||
'description' => t('Toggle display of the "More information about text formats" link.'),
|
||||
),
|
||||
);
|
||||
foreach ($entities as $type => $info) {
|
||||
if ($info['fieldable']) {
|
||||
$perms['show format selection for ' . $type] = array(
|
||||
'title' => t('Show format selection for @entitys', array('@entity' => $type)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $perms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function better_formats_menu() {
|
||||
$items = array();
|
||||
$items['admin/config/content/formats/settings'] = array(
|
||||
'title' => 'Settings',
|
||||
'description' => 'Manage text formats',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('better_formats_admin_settings_form'),
|
||||
'access arguments' => array('administer filters'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 3,
|
||||
'file' => 'better_formats.admin_settings.inc',
|
||||
);
|
||||
/*
|
||||
$items['admin/config/content/formats/defaults'] = array(
|
||||
'title' => 'Defaults',
|
||||
'description' => 'Manage text formats',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('better_formats_defaults_admin_form'),
|
||||
'access arguments' => array('administer filters'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 2,
|
||||
'file' => 'better_formats.admin_defaults.inc',
|
||||
);
|
||||
*/
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements of hook_element_info_alter().
|
||||
*/
|
||||
function better_formats_element_info_alter(&$type) {
|
||||
// Our process callback must run immediately after filter_process_format().
|
||||
$filter_process_format_location = array_search('filter_process_format', $type['text_format']['#process']);
|
||||
$replacement = array('filter_process_format', 'better_formats_filter_process_format');
|
||||
array_splice($type['text_format']['#process'], $filter_process_format_location, 1, $replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process callback for form elements that have a text format selector attached.
|
||||
*
|
||||
* This callback runs after filter_process_format() and performs additional
|
||||
* modifications to the form element.
|
||||
*
|
||||
* @see filter_process_format()
|
||||
*/
|
||||
function better_formats_filter_process_format($element) {
|
||||
// Before we make any modifications to the element, record whether or not
|
||||
// filter_process_format() has determined that (for security reasons) the
|
||||
// user is not allowed to make any changes to this field. (This will happen
|
||||
// if the user does not have permission to use the currently-assigned text
|
||||
// format.)
|
||||
$access_denied_for_security = isset($element['format']['#access']) && !$element['format']['#access'];
|
||||
|
||||
// Now hide several parts of the element for cosmetic reasons (depending on
|
||||
// the permissions of the current user).
|
||||
$show_selection = TRUE;
|
||||
if (isset($element['#entity_type'])) {
|
||||
$show_selection = user_access('show format selection for ' . $element['#entity_type']);
|
||||
}
|
||||
$show_tips = user_access('show format tips');
|
||||
$show_tips_link = user_access('show more format tips link');
|
||||
if (!$show_selection) {
|
||||
$element['format']['format']['#access'] = FALSE;
|
||||
}
|
||||
if (!$show_tips) {
|
||||
$element['format']['guidelines']['#access'] = FALSE;
|
||||
}
|
||||
if (!$show_tips_link) {
|
||||
$element['format']['help']['#access'] = FALSE;
|
||||
}
|
||||
|
||||
// If the element represents a field attached to an entity, we may need to
|
||||
// adjust the allowed text format options. However, we don't want to touch
|
||||
// this if filter_process_format() has determined that (for security reasons)
|
||||
// the user is not allowed to make any changes; in that case, Drupal core
|
||||
// will hide the format selector and force the field to be saved with its
|
||||
// current values, and we should not do anything to alter that process.
|
||||
if (isset($element['#entity_type']) && !$access_denied_for_security) {
|
||||
$instance_info = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
|
||||
$bf = isset($instance_info['settings']['better_formats']) ? $instance_info['settings']['better_formats'] : NULL;
|
||||
|
||||
// Need to only do this on create forms.
|
||||
if (!empty($element['#entity']) && !empty($element['#entity_type'])) {
|
||||
list($eid, $vid, $bundle) = entity_extract_ids($element['#entity_type'], $element['#entity']);
|
||||
if (empty($eid) && isset($bf) && !empty($bf['default_order_toggle']) && !empty($bf['default_order_wrapper']['formats'])) {
|
||||
$order = $bf['default_order_wrapper']['formats'];
|
||||
uasort($order, 'better_formats_text_format_sort');
|
||||
|
||||
$options = array();
|
||||
foreach ($order as $id => $weight) {
|
||||
if (isset($element['format']['format']['#options'][$id])) {
|
||||
$options[$id] = $element['format']['format']['#options'][$id];
|
||||
}
|
||||
}
|
||||
$element['format']['format']['#options'] = $options;
|
||||
$options_keys = array_keys($options);
|
||||
$element['format']['format']['#default_value'] = array_shift($options_keys);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($bf) && !empty($bf['allowed_formats_toggle']) && !empty($bf['allowed_formats'])) {
|
||||
// Filter the list of available formats to those allowed on this field.
|
||||
$allowed_fields = array_filter($bf['allowed_formats']);
|
||||
$options = &$element['format']['format']['#options'];
|
||||
$options = array_intersect_key($options, $allowed_fields);
|
||||
|
||||
// If there is only one allowed format, deny access to the text format
|
||||
// selector for cosmetic reasons, just like filter_process_format() does.
|
||||
if (count($options) == 1) {
|
||||
$element['format']['format']['#access'] = FALSE;
|
||||
$show_selection = FALSE;
|
||||
}
|
||||
|
||||
// If there are no allowed formats, we need to deny access to the entire
|
||||
// field, since it doesn't make sense to add or edit content that does
|
||||
// not have a text format.
|
||||
if (empty($options)) {
|
||||
$element['#access'] = FALSE;
|
||||
}
|
||||
// Otherwise, if the current default format is no longer one of the
|
||||
// allowed options, a new default format must be assigned.
|
||||
elseif (!isset($options[$element['format']['format']['#default_value']])) {
|
||||
// If there is no text in the field, it is safe to automatically assign
|
||||
// a new default format. We pick the first available option to be
|
||||
// consistent with what filter_default_format() does.
|
||||
if (!isset($element['value']['#default_value']) || $element['value']['#default_value']==='') {
|
||||
$formats = array_keys($options);
|
||||
$element['format']['format']['#default_value'] = reset($formats);
|
||||
}
|
||||
// Otherwise, it is unsafe to automatically assign a new default format
|
||||
// (since this will display the content in a way that was not
|
||||
// originally intended and might be dangerous, e.g. if the content
|
||||
// contains an attempted XSS attack). A human must explicitly decide
|
||||
// which new format to assign, so we force the field to be required but
|
||||
// with no default value, similar to what filter_process_format() does.
|
||||
// Although filter_process_format() limits this functionality to users
|
||||
// with the 'administer filters' permission, we can allow it for any
|
||||
// user here since we know that the user already has permission to use
|
||||
// the current format; thus, there is no danger of exposing unformatted
|
||||
// text (for example, raw PHP code) that they are otherwise not allowed
|
||||
// to see.
|
||||
else {
|
||||
$element['format']['format']['#required'] = TRUE;
|
||||
$element['format']['format']['#default_value'] = NULL;
|
||||
// Force access to the format selector (it may have been denied
|
||||
// previously for cosmetic reasons).
|
||||
$element['format']['#access'] = TRUE;
|
||||
$element['format']['format']['#access'] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the user is not supposed to see the text format selector, hide all
|
||||
// guidelines except those associated with the default format. We need to do
|
||||
// this at the end, since the above code may have altered the default format.
|
||||
if (!$show_selection && isset($element['format']['format']['#default_value'])) {
|
||||
foreach (element_children($element['format']['guidelines']) as $format) {
|
||||
if ($format != $element['format']['format']['#default_value']) {
|
||||
$element['format']['guidelines'][$format]['#access'] = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the entire text format fieldset if the user is not supposed to see
|
||||
// anything inside it.
|
||||
if (!$show_selection && !$show_tips && !$show_tips_link) {
|
||||
$element['format']['#type'] = 'container';
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort text formats by weight.
|
||||
*/
|
||||
function better_formats_text_format_sort($a, $b) {
|
||||
return $a['weight'] > $b['weight'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
function better_formats_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
|
||||
$settings = $form['#instance']['settings'];
|
||||
// Only alter fields with text processing and if admin has chosen.
|
||||
$text_processing = isset($settings['text_processing']);
|
||||
if ($text_processing && variable_get('better_formats_per_field_core', 0)) {
|
||||
// Add a submit handler to save default values on empty fields.
|
||||
$form['#submit'][] = 'better_formats_form_field_ui_edit_form_submit';
|
||||
}
|
||||
|
||||
// If the field is a format-using text field, allow the admin to configure
|
||||
// which formats are allowed here.
|
||||
if ($text_processing) {
|
||||
// We have to set an explicit weight here so that we can put the allowed
|
||||
// formats list after it.
|
||||
$form['instance']['settings']['text_processing']['#weight'] = -3;
|
||||
|
||||
$bf_settings = isset($settings['better_formats']) ? $settings['better_formats'] : array();
|
||||
// Add in our formats table
|
||||
$form['instance']['settings'] += better_formats_field_settings_form($bf_settings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the settings form for Field API fields.
|
||||
*
|
||||
* @param $bf_form
|
||||
* The existing better formats settings form from the form element.
|
||||
*/
|
||||
function better_formats_field_settings_form($bf_form = array()) {
|
||||
$formats = filter_formats();
|
||||
$form = array();
|
||||
$form['better_formats'] = array(
|
||||
'#tree' => TRUE,
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Text Formats'),
|
||||
'#weight' => -2,
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="instance[settings][text_processing]"]' => array('value' => '1'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($formats as $format_id => $format) {
|
||||
$allowed_options[$format_id] = $format->name;
|
||||
}
|
||||
|
||||
$allowed_toggle_default = isset($bf_form['allowed_formats_toggle']) ? $bf_form['allowed_formats_toggle'] : FALSE;
|
||||
$allowed_defaults = isset($bf_form['allowed_formats']) ? $bf_form['allowed_formats'] : array();
|
||||
if (empty($allowed_defaults)) {
|
||||
$allowed_defaults = array_keys($allowed_options);
|
||||
}
|
||||
|
||||
$form['better_formats']['allowed_formats_toggle'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Limit allowed text formats'),
|
||||
'#description' => t('Check the allowed formats below. If checked available text formats can be chosen.'),
|
||||
'#weight' => 1,
|
||||
'#default_value' => $allowed_toggle_default,
|
||||
);
|
||||
$form['better_formats']['allowed_formats'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Allowed formats'),
|
||||
'#options' => $allowed_options,
|
||||
'#description' => t('Select the text formats allowed for this field. Note that not all of these may appear on the form if a user does not have permission to use them. <strong>Warning:</strong> This affects existing content which may leave you unable to edit some fields. If that happens you must allow the format that field was saved in here.'),
|
||||
'#weight' => 2,
|
||||
'#default_value' => $allowed_defaults,
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="instance[settings][text_processing]"]' => array('value' => '1'),
|
||||
':input[name="instance[settings][better_formats][allowed_formats_toggle]"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$order_toggle_default = isset($bf_form['default_order_toggle']) ? $bf_form['default_order_toggle'] : FALSE;
|
||||
$form['better_formats']['default_order_toggle'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Override default order'),
|
||||
'#description' => t('Override the global order that will determine the default text format a user will get <strong>only on entity creation</strong>.'),
|
||||
'#weight' => 3,
|
||||
'#default_value' => $order_toggle_default,
|
||||
);
|
||||
|
||||
$form['better_formats']['default_order_wrapper'] = array(
|
||||
//'#tree' => TRUE,
|
||||
'#type' => 'container',
|
||||
'#theme' => 'better_formats_field_default_order',
|
||||
'#weight' => 4,
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="instance[settings][text_processing]"]' => array('value' => '1'),
|
||||
':input[name="instance[settings][better_formats][default_order_toggle]"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($formats as $key => $format) {
|
||||
$default = isset($bf_form['default_order_wrapper']['formats'][$key]) ? $bf_form['default_order_wrapper']['formats'][$key] : NULL;
|
||||
$rows[$key]['name'] = array('#markup' => $format->name);
|
||||
$rows[$key]['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#default_value' => isset($default['weight']) ? $default['weight'] : $format->weight,
|
||||
'#delta' => 50,
|
||||
);
|
||||
$rows[$key]['#weight'] = isset($default['weight']) ? $default['weight'] : $format->weight;
|
||||
}
|
||||
$form['better_formats']['default_order_wrapper']['formats'] = $rows;
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for field instance edit form.
|
||||
*
|
||||
* Copied and slightly modifed from field_ui_field_edit_form_submit().
|
||||
* @see field_ui_field_edit_form_submit()
|
||||
*/
|
||||
function better_formats_form_field_ui_edit_form_submit($form, &$form_state) {
|
||||
$instance = $form_state['values']['instance'];
|
||||
$field = $form_state['values']['field'];
|
||||
|
||||
// Only act on fields that have text processing enabled.
|
||||
if ($instance['settings']['text_processing'] == 1) {
|
||||
// Update any field settings that have changed.
|
||||
$field_source = field_info_field($instance['field_name']);
|
||||
$field = array_merge($field_source, $field);
|
||||
field_update_field($field);
|
||||
|
||||
// Handle the default value.
|
||||
if (isset($form['instance']['default_value_widget'])) {
|
||||
$element = $form['instance']['default_value_widget'];
|
||||
|
||||
// Extract field values.
|
||||
$items = array();
|
||||
field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
|
||||
// Commenting out the below line to not remove emtpy fields.
|
||||
//field_default_submit(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
|
||||
|
||||
$instance['default_value'] = $items ? $items : NULL;
|
||||
}
|
||||
|
||||
// Retrieve the stored instance settings to merge with the incoming values.
|
||||
$instance_source = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
|
||||
$instance = array_merge($instance_source, $instance);
|
||||
field_update_instance($instance);
|
||||
}
|
||||
// Messaging and redirect removed as this is added in the default handler.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme()
|
||||
*/
|
||||
function better_formats_theme() {
|
||||
return array(
|
||||
'better_formats_field_default_order' => array(
|
||||
'render element' => 'form',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme format default by role on field settings form.
|
||||
*/
|
||||
function theme_better_formats_field_default_order(&$variables) {
|
||||
$form = $variables['form'];
|
||||
|
||||
$rows = array();
|
||||
$output = drupal_render($form['override']);
|
||||
if (is_array($form)) {
|
||||
$order_form =& $form['formats'];
|
||||
$order_form['#sorted'] = FALSE;
|
||||
foreach (element_children($order_form, TRUE) as $name) {
|
||||
$order_form[$name]['weight']['#attributes']['class'][] = 'format-order-weight';
|
||||
$rows[] = array(
|
||||
'data' => array(
|
||||
drupal_render($order_form[$name]['name']),
|
||||
drupal_render($order_form[$name]['weight']),
|
||||
),
|
||||
'class' => array('draggable'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$header = array(
|
||||
t('Format'),
|
||||
t('Weight'),
|
||||
);
|
||||
|
||||
$output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'format-order')));
|
||||
drupal_add_tabledrag('format-order', 'order', 'sibling', 'format-order-weight', NULL, NULL, TRUE);
|
||||
|
||||
return $output;
|
||||
}
|
339
sites/all/modules/contrib/editor/pathologic/LICENSE.txt
Normal file
339
sites/all/modules/contrib/editor/pathologic/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
21
sites/all/modules/contrib/editor/pathologic/README.txt
Normal file
21
sites/all/modules/contrib/editor/pathologic/README.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
Pathologic
|
||||
----------
|
||||
|
||||
Project Page:
|
||||
http://drupal.org/project/pathologic
|
||||
|
||||
By Garrett Albright
|
||||
http://drupal.org/user/191212
|
||||
|
||||
Originally sponsored by Precision Intermedia
|
||||
http://www.precisionintermedia.com/
|
||||
|
||||
Thanks to all who have used this module over the years and provided bug reports
|
||||
and suggestions via email and the issue queue! I love you all.
|
||||
|
||||
Installation & Configuration
|
||||
----------------------------
|
||||
|
||||
For full installation and configuration instructions, please see this page in
|
||||
the Drupal online manual:
|
||||
http://drupal.org/node/257026
|
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by Pathologic.
|
||||
*
|
||||
* @ingroup pathologic
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allow modules to alter a URL Pathologic is about to create.
|
||||
*
|
||||
* This hook is invoked after Pathologic has torn apart a URL it thinks it can
|
||||
* alter properly and is just about to call the url() function to construct the
|
||||
* new URL. Modules can alter the values that Pathologic is about to send to
|
||||
* url(), or even stop Pathologic from altering a URL entirely.
|
||||
*
|
||||
* @param $url_params
|
||||
* An array with 'path' and 'options' values, which correspond to the $path
|
||||
* and $options parameters of the url() function. The 'options' array has an
|
||||
* extra parameter labeled 'use_original' which is set to FALSE by default.
|
||||
* This parameter is ignored by url(), but if its value is set to TRUE after
|
||||
* all alter hook invocations, Pathologic will return the original, unaltered
|
||||
* path it found in the content instead of calling url() and generating a new
|
||||
* one. Thus, it provides a way for modules to halt the alteration of paths
|
||||
* which Pathologic has incorrectly decided should be altered.
|
||||
* @param $parts
|
||||
* This array contains the result of running parse_url() on the path that
|
||||
* Pathologic found in content, though Pathologic likely altered some of the
|
||||
* values in this array since. It contains another parameter, 'original',
|
||||
* which contains the original URL Pathologic found in the content, unaltered.
|
||||
* You should not alter this value in any way; to alter how Pathologic
|
||||
* constructs the new URL, alter $url_params instead.
|
||||
* @param $settings
|
||||
* This contains the settings Pathologic is using to decide how to alter the
|
||||
* URL; some settings are from the graphical filter form and alterable by the
|
||||
* user, while others are determined programmatically. If you're looking for
|
||||
* the filter settings which Pathologic is currently using (if you've altered
|
||||
* your own field onto the filter settings form, for example), try looking in
|
||||
* $settings['current_settings'].
|
||||
*
|
||||
* @see url()
|
||||
* @see parse_url()
|
||||
* @see pathologic_replace()
|
||||
* @see http://drupal.org/node/1762022
|
||||
*/
|
||||
function hook_pathologic_alter(&$url_params, $parts, $settings) {
|
||||
// If we're linking to the "bananas" subdirectory or something under it, then
|
||||
// have Pathologic pass through the original URL, without altering it.
|
||||
if (preg_match('~^bananas(/.*)?$~', $url_params['path'])) {
|
||||
$url_params['options']['use_original'] = TRUE;
|
||||
}
|
||||
|
||||
// If we're linking to a path like "article/something.html", then prepend
|
||||
// "magazine" to the path, but remove the ".html". The end result will look
|
||||
// like "magazine/article/something".
|
||||
if (preg_match('~^article/(.+)\.html$~', $url_params['path'], $matches)) {
|
||||
$url_params['path'] = 'magazine/article/' . $matches[1];
|
||||
}
|
||||
|
||||
// If the URL doesn't have a "foo" query parameter, then add one.
|
||||
if (!is_array($url_params['options']['query'])) {
|
||||
$url_params['options']['query'] = array();
|
||||
}
|
||||
if (empty($url_params['options']['query']['foo'])) {
|
||||
$url_params['options']['query']['foo'] = 'bar';
|
||||
}
|
||||
|
||||
// If it's a path to a local image, make sure it's using our CDN server.
|
||||
if (preg_match('~\.(png|gif|jpe?g)$', $url_params['path'])) {
|
||||
$url_params['path'] = 'http://cdn.example.com/' . $url_params['path'];
|
||||
$url_params['options']['external'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
13
sites/all/modules/contrib/editor/pathologic/pathologic.info
Normal file
13
sites/all/modules/contrib/editor/pathologic/pathologic.info
Normal file
@@ -0,0 +1,13 @@
|
||||
name = Pathologic
|
||||
description = Helps avoid broken links and incorrect paths in content.
|
||||
package = "Input filters"
|
||||
dependencies[] = filter
|
||||
core = 7.x
|
||||
files[] = pathologic.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-07-09
|
||||
version = "7.x-2.11"
|
||||
core = "7.x"
|
||||
project = "pathologic"
|
||||
datestamp = "1373385363"
|
||||
|
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* .install file for Pathologic.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Re-enable Pathologic under Drupal 7, preserving settings from Drupal 6.
|
||||
*/
|
||||
function pathologic_update_7000($sandbox) {
|
||||
// Make sure {d6_upgrade_filter} exists. It won't exist for people upgrading
|
||||
// from beta versions of the D7 version of Pathologic on native D7 sites (not
|
||||
// upgraded from D6).
|
||||
if (db_table_exists('d6_upgrade_filter')) {
|
||||
// Get all Pathologic data from {d6_upgrade_filter}.
|
||||
$rez = db_select('d6_upgrade_filter', 'dup')
|
||||
->fields('dup')
|
||||
->condition('module', 'pathologic')
|
||||
->execute();
|
||||
while ($instance = $rez->fetchObject()) {
|
||||
// Load the format
|
||||
if ($format = filter_format_load($instance->format)) {
|
||||
// Load filters.
|
||||
$format->filters = array();
|
||||
// Add the filters
|
||||
foreach (filter_list_format($instance->format) as $filter_name => $filter) {
|
||||
$format->filters[$filter_name] = (array)$filter;
|
||||
}
|
||||
// Add Pathologic
|
||||
$format->filters['pathologic'] = array(
|
||||
'weight' => $instance->weight,
|
||||
'status' => 1,
|
||||
'settings' => array(
|
||||
'absolute' => variable_get('filter_pathologic_absolute_' . $instance->format, TRUE),
|
||||
'local_paths' => variable_get('filter_pathologic_local_paths_' . $instance->format, ''),
|
||||
),
|
||||
);
|
||||
// Save the format
|
||||
filter_format_save($format);
|
||||
// Unset old variables
|
||||
variable_del('filter_pathologic_absolute_' . $instance->format);
|
||||
variable_del('filter_pathologic_local_paths_' . $instance->format);
|
||||
}
|
||||
}
|
||||
// Delete Pathologic data from {d6_upgrade_filter}…?
|
||||
// No, maybe we don't want to actually do that…?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert obsolete "absolute" setting to modern "protocol_style" setting for
|
||||
* each filter instance.
|
||||
*/
|
||||
function pathologic_update_7200(&$sandbox) {
|
||||
foreach (filter_formats() as $format) {
|
||||
// @see http://drupal.org/node/1304930
|
||||
if (empty($format->filters)) {
|
||||
$format->filters = array();
|
||||
// Add the filters
|
||||
foreach (filter_list_format($format->format) as $filter_name => $filter) {
|
||||
$format->filters[$filter_name] = (array)$filter;
|
||||
}
|
||||
}
|
||||
if (isset($format->filters['pathologic'])) {
|
||||
$format->filters['pathologic']['settings']['protocol_style'] = $format->filters['pathologic']['settings']['absolute'] ? 'full' : 'path';
|
||||
unset($format->filters['pathologic']['settings']['absolute']);
|
||||
filter_format_save($format);
|
||||
}
|
||||
}
|
||||
}
|
448
sites/all/modules/contrib/editor/pathologic/pathologic.module
Normal file
448
sites/all/modules/contrib/editor/pathologic/pathologic.module
Normal file
@@ -0,0 +1,448 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Pathologic text filter for Drupal.
|
||||
*
|
||||
* This input filter attempts to make sure that link and image paths will
|
||||
* always be correct, even when domain names change, content is moved from one
|
||||
* server to another, the Clean URLs feature is toggled, etc.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_filter_info().
|
||||
*/
|
||||
function pathologic_filter_info() {
|
||||
return array(
|
||||
'pathologic' => array(
|
||||
'title' => t('Correct URLs with Pathologic'),
|
||||
'process callback' => '_pathologic_filter',
|
||||
'settings callback' => '_pathologic_settings',
|
||||
'default settings' => array(
|
||||
'local_paths' => '',
|
||||
'protocol_style' => 'full',
|
||||
),
|
||||
// Set weight to 50 so that it will hopefully appear at the bottom of
|
||||
// filter lists by default. 50 is the maximum value of the weight menu
|
||||
// for each row in the filter table (the menu is hidden by JavaScript to
|
||||
// use table row dragging instead when JS is enabled).
|
||||
'weight' => 50,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings callback for Pathologic.
|
||||
*/
|
||||
function _pathologic_settings($form, &$form_state, $filter, $format, $defaults, $filters) {
|
||||
return array(
|
||||
'reminder' => array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('In most cases, Pathologic should be the <em>last</em> filter in the “Filter processing order” list.'),
|
||||
'#weight' => -10,
|
||||
),
|
||||
'protocol_style' => array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Processed URL format'),
|
||||
'#default_value' => isset($filter->settings['protocol_style']) ? $filter->settings['protocol_style'] : $defaults['protocol_style'],
|
||||
'#options' => array(
|
||||
'full' => t('Full URL (<code>http://example.com/foo/bar</code>)'),
|
||||
'proto-rel' => t('Protocol relative URL (<code>//example.com/foo/bar</code>)'),
|
||||
'path' => t('Path relative to server root (<code>/foo/bar</code>)'),
|
||||
),
|
||||
'#description' => t('The <em>Full URL</em> option is best for stopping broken images and links in syndicated content (such as in RSS feeds), but will likely lead to problems if your site is accessible by both HTTP and HTTPS. Paths output with the <em>Protocol relative URL</em> option will avoid such problems, but feed readers and other software not using up-to-date standards may be confused by the paths. The <em>Path relative to server root</em> option will avoid problems with sites accessible by both HTTP and HTTPS with no compatibility concerns, but will absolutely not fix broken images and links in syndicated content.'),
|
||||
'#weight' => 10,
|
||||
),
|
||||
'local_paths' => array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('All base paths for this site'),
|
||||
'#default_value' => isset($filter->settings['local_paths']) ? $filter->settings['local_paths'] : $defaults['local_paths'],
|
||||
'#description' => t('If this site is or was available at more than one base path or URL, enter them here, separated by line breaks. For example, if this site is live at <code>http://example.com/</code> but has a staging version at <code>http://dev.example.org/staging/</code>, you would enter both those URLs here. If confused, please read <a href="!docs">Pathologic’s documentation</a> for more information about this option and what it affects.', array('!docs' => 'http://drupal.org/node/257026')),
|
||||
'#weight' => 20,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pathologic filter callback.
|
||||
*
|
||||
* Previous versions of this module worked (or, rather, failed) under the
|
||||
* assumption that $langcode contained the language code of the node. Sadly,
|
||||
* this isn't the case.
|
||||
* @see http://drupal.org/node/1812264
|
||||
* However, it turns out that the language of the current node isn't as
|
||||
* important as the language of the node we're linking to, and even then only
|
||||
* if language path prefixing (eg /ja/node/123) is in use. REMEMBER THIS IN THE
|
||||
* FUTURE, ALBRIGHT.
|
||||
*
|
||||
* @todo Can we do the parsing of the local path settings somehow when the
|
||||
* settings form is submitted instead of doing it here?
|
||||
*/
|
||||
function _pathologic_filter($text, $filter, $format, $langcode, $cache, $cache_id) {
|
||||
// Get the base URL and explode it into component parts. We add these parts
|
||||
// to the exploded local paths settings later.
|
||||
global $base_url;
|
||||
$base_url_parts = parse_url($base_url . '/');
|
||||
// Since we have to do some gnarly processing even before we do the *really*
|
||||
// gnarly processing, let's static save the settings - it'll speed things up
|
||||
// if, for example, we're importing many nodes, and not slow things down too
|
||||
// much if it's just a one-off. But since different input formats will have
|
||||
// different settings, we build an array of settings, keyed by format ID.
|
||||
$settings = &drupal_static(__FUNCTION__, array());
|
||||
if (!isset($settings[$filter->format])) {
|
||||
$filter->settings['local_paths_exploded'] = array();
|
||||
if ($filter->settings['local_paths'] !== '') {
|
||||
// Build an array of the exploded local paths for this format's settings.
|
||||
// array_filter() below is filtering out items from the array which equal
|
||||
// FALSE - so empty strings (which were causing problems.
|
||||
// @see http://drupal.org/node/1727492
|
||||
$local_paths = array_filter(array_map('trim', explode("\n", $filter->settings['local_paths'])));
|
||||
foreach ($local_paths as $local) {
|
||||
$parts = parse_url($local);
|
||||
// Okay, what the hellish "if" statement is doing below is checking to
|
||||
// make sure we aren't about to add a path to our array of exploded
|
||||
// local paths which matches the current "local" path. We consider it
|
||||
// not a match, if…
|
||||
// @todo: This is pretty horrible. Can this be simplified?
|
||||
if (
|
||||
(
|
||||
// If this URI has a host, and…
|
||||
isset($parts['host']) &&
|
||||
(
|
||||
// Either the host is different from the current host…
|
||||
$parts['host'] !== $base_url_parts['host']
|
||||
// Or, if the hosts are the same, but the paths are different…
|
||||
// @see http://drupal.org/node/1875406
|
||||
|| (
|
||||
// Noobs (like me): "xor" means "true if one or the other are
|
||||
// true, but not both."
|
||||
(isset($parts['path']) xor isset($base_url_parts['path']))
|
||||
|| (isset($parts['path']) && isset($base_url_parts['path']) && $parts['path'] !== $base_url_parts['path'])
|
||||
)
|
||||
)
|
||||
) ||
|
||||
// Or…
|
||||
(
|
||||
// The URI doesn't have a host…
|
||||
!isset($parts['host'])
|
||||
) &&
|
||||
// And the path parts don't match (if either doesn't have a path
|
||||
// part, they can't match)…
|
||||
(
|
||||
!isset($parts['path']) ||
|
||||
!isset($base_url_parts['path']) ||
|
||||
$parts['path'] !== $base_url_parts['path']
|
||||
)
|
||||
) {
|
||||
// Add it to the list.
|
||||
$filter->settings['local_paths_exploded'][] = $parts;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now add local paths based on "this" server URL.
|
||||
$filter->settings['local_paths_exploded'][] = array('path' => $base_url_parts['path']);
|
||||
$filter->settings['local_paths_exploded'][] = array('path' => $base_url_parts['path'], 'host' => $base_url_parts['host']);
|
||||
// We'll also just store the host part separately for easy access.
|
||||
$filter->settings['base_url_host'] = $base_url_parts['host'];
|
||||
|
||||
$settings[$filter->format] = $filter->settings;
|
||||
}
|
||||
// Get the language code for the text we're about to process.
|
||||
$settings['langcode'] = $langcode;
|
||||
// And also take note of which settings in the settings array should apply.
|
||||
$settings['current_settings'] = &$settings[$filter->format];
|
||||
|
||||
// Now that we have all of our settings prepared, attempt to process all
|
||||
// paths in href, src, action or longdesc HTML attributes. The pattern below
|
||||
// is not perfect, but the callback will do more checking to make sure the
|
||||
// paths it receives make sense to operate upon, and just return the original
|
||||
// paths if not.
|
||||
return preg_replace_callback('~(href|src|action|longdesc)="([^"]+)~i', '_pathologic_replace', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process and replace paths. preg_replace_callback() callback.
|
||||
*/
|
||||
function _pathologic_replace($matches) {
|
||||
// Get the settings for the filter. Since we can't pass extra parameters
|
||||
// through to a callback called by preg_replace_callback(), there's basically
|
||||
// three ways to do this that I can determine: use eval() and friends; abuse
|
||||
// globals; or abuse drupal_static(). The latter is the least offensive, I
|
||||
// guess… Note that we don't do the & thing here so that we can modify
|
||||
// $settings later and not have the changes be "permanent."
|
||||
$settings = drupal_static('_pathologic_filter');
|
||||
// If it appears the path is a scheme-less URL, prepend a scheme to it.
|
||||
// parse_url() cannot properly parse scheme-less URLs. Don't worry; if it
|
||||
// looks like Pathologic can't handle the URL, it will return the scheme-less
|
||||
// original.
|
||||
|
||||
// @see https://drupal.org/node/1617944
|
||||
// @see https://drupal.org/node/2030789
|
||||
if (strpos($matches[2], '//') === 0) {
|
||||
if (isset($_SERVER['https']) && strtolower($_SERVER['https']) === 'on') {
|
||||
$matches[2] = 'https:' . $matches[2];
|
||||
}
|
||||
else {
|
||||
$matches[2] = 'http:' . $matches[2];
|
||||
}
|
||||
}
|
||||
// Now parse the URL after reverting HTML character encoding.
|
||||
// @see http://drupal.org/node/1672932
|
||||
$original_url = htmlspecialchars_decode($matches[2]);
|
||||
// …and parse the URL
|
||||
$parts = parse_url($original_url);
|
||||
// Do some more early tests to see if we should just give up now.
|
||||
if (
|
||||
// If parse_url() failed, give up.
|
||||
$parts === FALSE
|
||||
|| (
|
||||
// If there's a scheme part and it doesn't look useful, bail out.
|
||||
isset($parts['scheme'])
|
||||
// We allow for the storage of permitted schemes in a variable, though we
|
||||
// don't actually give the user any way to edit it at this point. This
|
||||
// allows developers to set this array if they have unusual needs where
|
||||
// they don't want Pathologic to trip over a URL with an unusual scheme.
|
||||
// @see http://drupal.org/node/1834308
|
||||
// "files" and "internal" are for Path Filter compatibility.
|
||||
&& !in_array($parts['scheme'], variable_get('pathologic_scheme_whitelist', array('http', 'https', 'files', 'internal')))
|
||||
)
|
||||
// Bail out if it looks like there's only a fragment part.
|
||||
|| (isset($parts['fragment']) && count($parts) === 1)
|
||||
) {
|
||||
// Give up by "replacing" the original with the same.
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
if (isset($parts['path'])) {
|
||||
// Undo possible URL encoding in the path.
|
||||
// @see http://drupal.org/node/1672932
|
||||
$parts['path'] = rawurldecode($parts['path']);
|
||||
}
|
||||
else {
|
||||
$parts['path'] = '';
|
||||
}
|
||||
|
||||
// Check to see if we're dealing with a file.
|
||||
// @todo Should we still try to do path correction on these files too?
|
||||
if (isset($parts['scheme']) && $parts['scheme'] === 'files') {
|
||||
// Path Filter "files:" support. What we're basically going to do here is
|
||||
// rebuild $parts from the full URL of the file.
|
||||
$new_parts = parse_url(file_create_url(file_default_scheme() . '://' . $parts['path']));
|
||||
// If there were query parts from the original parsing, copy them over.
|
||||
if (!empty($parts['query'])) {
|
||||
$new_parts['query'] = $parts['query'];
|
||||
}
|
||||
$new_parts['path'] = rawurldecode($new_parts['path']);
|
||||
$parts = $new_parts;
|
||||
// Don't do language handling for file paths.
|
||||
$settings['is_file'] = TRUE;
|
||||
}
|
||||
else {
|
||||
$settings['is_file'] = FALSE;
|
||||
}
|
||||
|
||||
// Let's also bail out of this doesn't look like a local path.
|
||||
$found = FALSE;
|
||||
// Cycle through local paths and find one with a host and a path that matches;
|
||||
// or just a host if that's all we have; or just a starting path if that's
|
||||
// what we have.
|
||||
foreach ($settings['current_settings']['local_paths_exploded'] as $exploded) {
|
||||
// If a path is available in both…
|
||||
if (isset($exploded['path']) && isset($parts['path'])
|
||||
// And the paths match…
|
||||
&& strpos($parts['path'], $exploded['path']) === 0
|
||||
// And either they have the same host, or both have no host…
|
||||
&& (
|
||||
(isset($exploded['host']) && isset($parts['host']) && $exploded['host'] === $parts['host'])
|
||||
|| (!isset($exploded['host']) && !isset($parts['host']))
|
||||
)
|
||||
) {
|
||||
// Remove the shared path from the path. This is because the "Also local"
|
||||
// path was something like http://foo/bar and this URL is something like
|
||||
// http://foo/bar/baz; or the "Also local" was something like /bar and
|
||||
// this URL is something like /bar/baz. And we only care about the /baz
|
||||
// part.
|
||||
$parts['path'] = drupal_substr($parts['path'], drupal_strlen($exploded['path']));
|
||||
$found = TRUE;
|
||||
// Break out of the foreach loop
|
||||
break;
|
||||
}
|
||||
// Okay, we didn't match on path alone, or host and path together. Can we
|
||||
// match on just host? Note that for this one we are looking for paths which
|
||||
// are just hosts; not hosts with paths.
|
||||
elseif ((isset($parts['host']) && !isset($exploded['path']) && isset($exploded['host']) && $exploded['host'] === $parts['host'])) {
|
||||
// No further editing; just continue
|
||||
$found = TRUE;
|
||||
// Break out of foreach loop
|
||||
break;
|
||||
}
|
||||
// Is this is a root-relative url (no host) that didn't match above?
|
||||
// Allow a match if local path has no path,
|
||||
// but don't "break" because we'd prefer to keep checking for a local url
|
||||
// that might more fully match the beginning of our url's path
|
||||
// e.g.: if our url is /foo/bar we'll mark this as a match for
|
||||
// http://example.com but want to keep searching and would prefer a match
|
||||
// to http://example.com/foo if that's configured as a local path
|
||||
elseif (!isset($parts['host']) && (!isset($exploded['path']) || $exploded['path'] == '/')) {
|
||||
$found = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// If the path is not within the drupal root return original url, unchanged
|
||||
if (!$found) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
// Okay, format the URL.
|
||||
// If there's still a slash lingering at the start of the path, chop it off.
|
||||
$parts['path'] = ltrim($parts['path'],'/');
|
||||
|
||||
// Examine the query part of the URL. Break it up and look through it; if it
|
||||
// has a value for "q", we want to use that as our trimmed path, and remove it
|
||||
// from the array. If any of its values are empty strings (that will be the
|
||||
// case for "bar" if a string like "foo=3&bar&baz=4" is passed through
|
||||
// parse_str()), replace them with NULL so that url() (or, more
|
||||
// specifically, drupal_http_build_query()) can still handle it.
|
||||
if (isset($parts['query'])) {
|
||||
parse_str($parts['query'], $parts['qparts']);
|
||||
foreach ($parts['qparts'] as $key => $value) {
|
||||
if ($value === '') {
|
||||
$parts['qparts'][$key] = NULL;
|
||||
}
|
||||
elseif ($key === 'q') {
|
||||
$parts['path'] = $value;
|
||||
unset($parts['qparts']['q']);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$parts['qparts'] = NULL;
|
||||
}
|
||||
|
||||
// If we don't have a path yet, bail out.
|
||||
if (!isset($parts['path'])) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
// If we didn't previously identify this as a file, check to see if the file
|
||||
// exists now that we have the correct path relative to DRUPAL_ROOT
|
||||
if (!$settings['is_file']){
|
||||
$settings['is_file'] = !empty($parts['path']) && is_file(DRUPAL_ROOT . '/'. $parts['path']);
|
||||
}
|
||||
|
||||
// Okay, deal with language stuff.
|
||||
if ($settings['is_file']) {
|
||||
// If we're linking to a file, use a fake LANGUAGE_NONE language object.
|
||||
// Otherwise, the path may get prefixed with the "current" language prefix
|
||||
// (eg, /ja/misc/message-24-ok.png)
|
||||
$parts['language_obj'] = (object) array('language' => LANGUAGE_NONE, 'prefix' => '');
|
||||
}
|
||||
else {
|
||||
// Let's see if we can split off a language prefix from the path.
|
||||
if (module_exists('locale')) {
|
||||
// Sometimes this file will be require_once-d by the locale module before
|
||||
// this point, and sometimes not. We require_once it ourselves to be sure.
|
||||
require_once DRUPAL_ROOT . '/includes/language.inc';
|
||||
list($language_obj, $path) = language_url_split_prefix($parts['path'], language_list());
|
||||
if ($language_obj) {
|
||||
$parts['path'] = $path;
|
||||
$parts['language_obj'] = $language_obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we get to this point and $parts['path'] is now an empty string (which
|
||||
// will be the case if the path was originally just "/"), then we
|
||||
// want to link to <front>.
|
||||
if ($parts['path'] === '') {
|
||||
$parts['path'] = '<front>';
|
||||
}
|
||||
// Build the parameters we will send to url()
|
||||
$url_params = array(
|
||||
'path' => $parts['path'],
|
||||
'options' => array(
|
||||
'query' => $parts['qparts'],
|
||||
'fragment' => isset($parts['fragment']) ? $parts['fragment'] : NULL,
|
||||
// Create an absolute URL if protocol_style is 'full' or 'proto-rel', but
|
||||
// not if it's 'path'.
|
||||
'absolute' => $settings['current_settings']['protocol_style'] !== 'path',
|
||||
// If we seem to have found a language for the path, pass it along to
|
||||
// url(). Otherwise, ignore the 'language' parameter.
|
||||
'language' => isset($parts['language_obj']) ? $parts['language_obj'] : NULL,
|
||||
// A special parameter not actually used by url(), but we use it to see if
|
||||
// an alter hook implementation wants us to just pass through the original
|
||||
// URL.
|
||||
'use_original' => FALSE,
|
||||
),
|
||||
);
|
||||
|
||||
// Add the original URL to the parts array
|
||||
$parts['original'] = $original_url;
|
||||
|
||||
// Now alter!
|
||||
// @see http://drupal.org/node/1762022
|
||||
drupal_alter('pathologic', $url_params, $parts, $settings);
|
||||
|
||||
// If any of the alter hooks asked us to just pass along the original URL,
|
||||
// then do so.
|
||||
if ($url_params['options']['use_original']) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
// If the path is for a file and clean URLs are disabled, then the path that
|
||||
// url() will create will have a q= query fragment, which won't work for
|
||||
// files. To avoid that, we use this trick to temporarily turn clean URLs on.
|
||||
// This is horrible, but it seems to be the sanest way to do this.
|
||||
// @see http://drupal.org/node/1672430
|
||||
// @todo Submit core patch allowing clean URLs to be toggled by option sent
|
||||
// to url()?
|
||||
if (!empty($settings['is_file'])) {
|
||||
$settings['orig_clean_url'] = !empty($GLOBALS['conf']['clean_url']);
|
||||
if (!$settings['orig_clean_url']) {
|
||||
$GLOBALS['conf']['clean_url'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Now for the url() call. Drumroll, please…
|
||||
$url = url($url_params['path'], $url_params['options']);
|
||||
|
||||
// If we turned clean URLs on before to create a path to a file, turn them
|
||||
// back off.
|
||||
if ($settings['is_file'] && !$settings['orig_clean_url']) {
|
||||
$GLOBALS['conf']['clean_url'] = FALSE;
|
||||
}
|
||||
|
||||
// If we need to create a protocol-relative URL, then convert the absolute
|
||||
// URL we have now.
|
||||
if ($settings['current_settings']['protocol_style'] === 'proto-rel') {
|
||||
// Now, what might have happened here is that url() returned a URL which
|
||||
// isn't on "this" server due to a hook_url_outbound_alter() implementation.
|
||||
// We don't want to convert the URL in that case. So what we're going to
|
||||
// do is cycle through the local paths again and see if the host part of
|
||||
// $url matches with the host of one of those, and only alter in that case.
|
||||
$url_parts = parse_url($url);
|
||||
if (!empty($url_parts['host']) && $url_parts['host'] === $settings['current_settings']['base_url_host']) {
|
||||
$url = _pathologic_url_to_protocol_relative($url);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply HTML character encoding, as is required for HTML attributes.
|
||||
// @see http://drupal.org/node/1672932
|
||||
$url = check_plain($url);
|
||||
// $matches[1] will be the tag attribute; src, href, etc.
|
||||
return "{$matches[1]}=\"{$url}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a full URL with a protocol to a protocol-relative URL.
|
||||
*
|
||||
* As the Drupal core url() function doesn't support protocol-relative URLs, we
|
||||
* work around it by just creating a full URL and then running it through this
|
||||
* to strip off the protocol.
|
||||
*
|
||||
* Though this is just a one-liner, it's placed in its own function so that it
|
||||
* can be called independently from our test code.
|
||||
*/
|
||||
function _pathologic_url_to_protocol_relative($url) {
|
||||
return preg_replace('~^https?://~', '//', $url);
|
||||
}
|
276
sites/all/modules/contrib/editor/pathologic/pathologic.test
Normal file
276
sites/all/modules/contrib/editor/pathologic/pathologic.test
Normal file
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Pathologic behavior testing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests that Pathologic ain't broke.
|
||||
*
|
||||
* We extend FilterUnitTestCase because it has some nice methods that we also
|
||||
* want to be able to use.
|
||||
*
|
||||
* Note to self: The method to pass bits of text through are fail() or pass().
|
||||
*/
|
||||
class PathologicTestCase extends DrupalWebTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Pathologic path filtering',
|
||||
'description' => 'Test Pathologic’s path translation and conversion.',
|
||||
'group' => 'Filter',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('pathologic');
|
||||
}
|
||||
|
||||
function testPathologic() {
|
||||
// Start by testing our function to build protocol-relative URLs
|
||||
$this->assertEqual(
|
||||
_pathologic_url_to_protocol_relative('http://example.com/foo/bar'),
|
||||
'//example.com/foo/bar',
|
||||
t('Protocol-relative URL creation with http:// URL')
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_url_to_protocol_relative('https://example.org/baz'),
|
||||
'//example.org/baz',
|
||||
t('Protocol-relative URL creation with https:// URL')
|
||||
);
|
||||
|
||||
// Build a phony filter
|
||||
$filter = new stdClass;
|
||||
$filter->callback = '_pathologic';
|
||||
$filter->settings = array(
|
||||
'protocol_style' => 'full',
|
||||
'local_paths' => '',
|
||||
);
|
||||
$filter->format = 0;
|
||||
|
||||
// Build some paths to check against
|
||||
$test_paths = array(
|
||||
'foo' => array(
|
||||
'path' => 'foo',
|
||||
'opts' => array()
|
||||
),
|
||||
'foo/bar' => array(
|
||||
'path' => 'foo/bar',
|
||||
'opts' => array()
|
||||
),
|
||||
'foo/bar?baz' => array(
|
||||
'path' => 'foo/bar',
|
||||
'opts' => array('query' => array('baz' => NULL))
|
||||
),
|
||||
'foo/bar?baz=qux' => array(
|
||||
'path' => 'foo/bar',
|
||||
'opts' => array('query' => array('baz' => 'qux'))
|
||||
),
|
||||
'foo/bar#baz' => array(
|
||||
'path' => 'foo/bar',
|
||||
'opts' => array('fragment' => 'baz'),
|
||||
),
|
||||
'foo/bar?baz=qux&quux=quuux#quuuux' => array(
|
||||
'path' => 'foo/bar',
|
||||
'opts' => array(
|
||||
'query' => array('baz' => 'qux', 'quux' => 'quuux'),
|
||||
'fragment' => 'quuuux',
|
||||
),
|
||||
),
|
||||
'foo%20bar?baz=qux%26quux' => array(
|
||||
'path' => 'foo bar',
|
||||
'opts' => array(
|
||||
'query' => array('baz' => 'qux&quux'),
|
||||
),
|
||||
),
|
||||
'/' => array(
|
||||
'path' => '<front>',
|
||||
'opts' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
// Run tests with clean URLs both enabled and disabled
|
||||
foreach (array(TRUE, FALSE) as $clean_url) {
|
||||
variable_set('clean_url', $clean_url);
|
||||
// Run tests with absoulte filtering enabled and disabled
|
||||
foreach (array('full', 'proto-rel', 'path') as $protocol_style) {
|
||||
$filter->settings['protocol_style'] = $protocol_style;
|
||||
$filter->format++;
|
||||
$paths = array();
|
||||
foreach ($test_paths as $path => $args) {
|
||||
$args['opts']['absolute'] = $protocol_style !== 'path';
|
||||
$paths[$path] = _pathologic_content_url($args['path'], $args['opts']);
|
||||
if ($protocol_style === 'proto-rel') {
|
||||
$paths[$path] = _pathologic_url_to_protocol_relative($paths[$path]);
|
||||
}
|
||||
}
|
||||
$t10ns = array(
|
||||
'!clean' => $clean_url ? t('Yes') : t('No'),
|
||||
'!ps' => $protocol_style,
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="foo"><img src="foo/bar" /></a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . $paths['foo'] . '"><img src="' . $paths['foo/bar'] . '" /></a>',
|
||||
t('Simple paths. Clean URLs: !clean; protocol style: !ps.', $t10ns)
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<form action="foo/bar?baz"><IMG LONGDESC="foo/bar?baz=qux" /></a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<form action="' . $paths['foo/bar?baz'] . '"><IMG LONGDESC="' . $paths['foo/bar?baz=qux'] . '" /></a>',
|
||||
t('Paths with query string. Clean URLs: !clean; protocol style: !ps.', $t10ns)
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="foo/bar#baz">', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . $paths['foo/bar#baz'] . '">',
|
||||
t('Path with fragment. Clean URLs: !clean; protocol style: !ps.', $t10ns)
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="#foo">', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="#foo">',
|
||||
t('Fragment-only links. Clean URLs: !clean; protocol style: !ps.', $t10ns)
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="foo/bar?baz=qux&quux=quuux#quuuux">', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . $paths['foo/bar?baz=qux&quux=quuux#quuuux'] . '">',
|
||||
t('Path with query string and fragment. Clean URLs: !clean; protocol style: !ps.', $t10ns)
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="foo%20bar?baz=qux%26quux">', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . $paths['foo%20bar?baz=qux%26quux'] . '">',
|
||||
t('Path with URL encoded parts')
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="/"></a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . $paths['/'] . '"></a>',
|
||||
t('Path with just slash. Clean URLs: !clean; protocol style: !ps', $t10ns)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
global $base_path;
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="' . $base_path . 'foo">bar</a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . _pathologic_content_url('foo', array('absolute' => FALSE)) .'">bar</a>',
|
||||
t('Paths beginning with $base_path (like WYSIWYG editors like to make)')
|
||||
);
|
||||
global $base_url;
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="' . $base_url . '/foo">bar</a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . _pathologic_content_url('foo', array('absolute' => FALSE)) .'">bar</a>',
|
||||
t('Paths beginning with $base_url')
|
||||
);
|
||||
|
||||
// @see http://drupal.org/node/1617944
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="//example.com/foo">bar</a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="//example.com/foo">bar</a>',
|
||||
t('Off-site schemeless URLs (//example.com/foo) ignored')
|
||||
);
|
||||
|
||||
// Test internal: and all base paths
|
||||
$filter->settings = array(
|
||||
'protocol_style' => 'full',
|
||||
'local_paths' => "http://example.com/qux\nhttp://example.org\n/bananas",
|
||||
);
|
||||
$filter->format++;
|
||||
|
||||
// @see https://drupal.org/node/2030789
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="//example.org/foo">bar</a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . _pathologic_content_url('foo', array('absolute' => TRUE)) . '">bar</a>',
|
||||
t('On-site schemeless URLs processed')
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="internal:foo">', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . _pathologic_content_url('foo', array('absolute' => TRUE)) . '">',
|
||||
t('Path Filter compatibility (internal:)')
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="files:image.jpeg">', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . _pathologic_content_url(file_create_url('public://image.jpeg'), array('absolute' => TRUE, 'is_file' => TRUE)) . '">',
|
||||
t('Path Filter compatibility (files:)')
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="http://example.com/qux/foo"><img src="http://example.org/bar.jpeg" longdesc="/bananas/baz" /></a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . _pathologic_content_url('foo', array('absolute' => TRUE)) . '"><img src="' . _pathologic_content_url('bar.jpeg', array('absolute' => TRUE)) . '" longdesc="' . _pathologic_content_url('baz', array('absolute' => TRUE)) . '" /></a>',
|
||||
t('"All base paths for this site" functionality')
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="webcal:foo">bar</a>', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="webcal:foo">bar</a>',
|
||||
t('URLs with likely protocols are ignored')
|
||||
);
|
||||
// Test hook_pathologic_alter() implementation.
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="foo?test=add_foo_qpart">', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="' . _pathologic_content_url('foo', array('absolute' => TRUE, 'query' => array('test' => 'add_foo_qpart', 'foo' => 'bar'))) . '">',
|
||||
t('hook_pathologic_alter(): Alter $url_params')
|
||||
);
|
||||
$this->assertEqual(
|
||||
_pathologic_filter('<a href="bar?test=use_original">', $filter, NULL, LANGUAGE_NONE, NULL, NULL),
|
||||
'<a href="bar?test=use_original">',
|
||||
t('hook_pathologic_alter(): Passthrough with use_original option')
|
||||
);
|
||||
|
||||
// Test paths to existing files when clean URLs are disabled.
|
||||
// @see http://drupal.org/node/1672430
|
||||
variable_set('clean_url', FALSE);
|
||||
$filtered_tag = _pathologic_filter('<img src="misc/druplicon.png" />', $filter, NULL, LANGUAGE_NONE, NULL, NULL);
|
||||
$this->assertTrue(
|
||||
strpos($filtered_tag, 'q=') === FALSE,
|
||||
t('Paths to files don\'t have ?q= when clean URLs are off')
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around url() which does HTML entity decoding and encoding.
|
||||
*
|
||||
* Since Pathologic works with paths in content, it needs to decode paths which
|
||||
* have been HTML-encoded, and re-encode them when done. This is a wrapper
|
||||
* around url() which does the same thing so that we can expect the results
|
||||
* from it and from Pathologic to still match in our tests.
|
||||
*
|
||||
* @see url()
|
||||
* @see http://drupal.org/node/1672932
|
||||
* @see http://www.w3.org/TR/xhtml1/guidelines.html#C_12
|
||||
*/
|
||||
function _pathologic_content_url($path, $options) {
|
||||
// If we should pretend this is a path to a file, temporarily enable clean
|
||||
// URLs if necessary.
|
||||
// @see _pathologic_replace()
|
||||
// @see http://drupal.org/node/1672430
|
||||
if (!empty($options['is_file'])) {
|
||||
$options['orig_clean_url'] = !empty($GLOBALS['conf']['clean_url']);
|
||||
if (!$options['orig_clean_url']) {
|
||||
$GLOBALS['conf']['clean_url'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
$url = check_plain(url(htmlspecialchars_decode($path), $options));
|
||||
|
||||
if (!empty($options['is_file']) && !$options['orig_clean_url']) {
|
||||
$GLOBALS['conf']['clean_url'] = FALSE;
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_pathologic_alter(), for testing that functionality.
|
||||
*/
|
||||
function pathologic_pathologic_alter(&$url_params, $parts, $settings) {
|
||||
if (is_array($parts['qparts']) && isset($parts['qparts']['test'])) {
|
||||
if ($parts['qparts']['test'] === 'add_foo_qpart') {
|
||||
// Add a "foo" query part
|
||||
if (empty($url_params['options']['query'])) {
|
||||
$url_params['options']['query'] = array();
|
||||
}
|
||||
$url_params['options']['query']['foo'] = 'bar';
|
||||
}
|
||||
elseif ($parts['qparts']['test'] === 'use_original') {
|
||||
$url_params['options']['use_original'] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
339
sites/all/modules/contrib/editor/video_filter/LICENSE.txt
Normal file
339
sites/all/modules/contrib/editor/video_filter/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
87
sites/all/modules/contrib/editor/video_filter/README.txt
Normal file
87
sites/all/modules/contrib/editor/video_filter/README.txt
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
This is a highly flexible and easy extendable filter module to embed any type
|
||||
of video in your site using a simple tag. Other modules can add video
|
||||
sites/formats (called codecs) using an easy plug-in architecture.
|
||||
|
||||
========= Installation =========
|
||||
|
||||
Enable the module on the modules page.
|
||||
|
||||
Go to admin/config/content/formats and configure the text format(s) that should
|
||||
be allowed to use this filter. Check the box to enable Video Filter and save.
|
||||
Some simple settings are available if you configure the text format. There you
|
||||
can change the default size and auto play settings.
|
||||
|
||||
Make sure that Video Filter is processed before "Convert URLs to links".
|
||||
You can do this by dragging and dropping Video Filter to the top of the
|
||||
processing order list. Do this even if it's allready on top, just to make sure!
|
||||
|
||||
If you're using the "Limit allowed HTML tags" filter, make sure Video Filter is
|
||||
processed after that filter.
|
||||
|
||||
To enable WYSIWYG support, go to the WYSIWYG settings for each input format and
|
||||
enable the Video Filter button.
|
||||
|
||||
========= Usage =========
|
||||
|
||||
Single video: [video:url]
|
||||
This will output the video using the default settings.
|
||||
|
||||
Random video from multiple URL's: [video:url,url]
|
||||
This will output one of the specified videos each time.
|
||||
|
||||
You can also set some parameters in the call:
|
||||
|
||||
[video:url width:X height:Y align:left/right autoplay:1/0]
|
||||
This will override the default settings for this video.
|
||||
|
||||
========= Developers =========
|
||||
|
||||
This module calls hook_codec_info(), so you can add your own codecs.
|
||||
|
||||
Example:
|
||||
|
||||
function MODULE_codec_info() {
|
||||
$codecs = array();
|
||||
// You can offer multiple video formats in one module.
|
||||
$codecs['youtube'] = array(
|
||||
// Will be used some day in user information.
|
||||
'name' => t('YouTube'),
|
||||
|
||||
// Special instructions for end users. Optional.
|
||||
'instructions' => t('Any special instructions that users need to know about
|
||||
to get this codec working correctly.'),
|
||||
|
||||
// The callback that will output the right embed code.
|
||||
'callback' => 'MODULE_youtube',
|
||||
|
||||
// HTML5 callback, for returning something that's device agnostic.
|
||||
// @SEE video_filter_youtube_html5.
|
||||
'html5_callback' => 'MODULE_service_html5',
|
||||
|
||||
// Regexp can be an array. $video['codec']['delta'] will be set to the key.
|
||||
'regexp' => '/youtube\.com\/watch\?v=([a-z0-9]+)/i',
|
||||
|
||||
// Ratio for resizing within user-given width and height (ratio = width / height)
|
||||
'ratio' => 425 / 355,
|
||||
);
|
||||
return $codecs;
|
||||
}
|
||||
|
||||
And this will be your callback function:
|
||||
|
||||
function MODULE_youtube($video) {
|
||||
// $video contains the video URL in source, the codec (as above) and also
|
||||
// [code][matches] with the result of the regexp and [codec][delta] with the
|
||||
// key of the matched regexp.
|
||||
$video['source'] = 'http://www.youtube.com/v/' . $video['codec']['matches'][1] . ($video['autoplay'] ? '&autoplay=1' : '');
|
||||
|
||||
// Outputs a general <object...> for embedding flash players. Needs width,
|
||||
// height, source and optionally align (left or right) and params (a list of
|
||||
// <param...> attributes)
|
||||
return video_filter_flash($video);
|
||||
}
|
||||
|
||||
========= Troubleshooting =========
|
||||
|
||||
If videos don't show up, try disabling any browser plugins, such as AdBlock.
|
@@ -0,0 +1,35 @@
|
||||
|
||||
##############################################
|
||||
## ONLY if you use ckeditor WITHOUT wysiwyg ##
|
||||
##############################################
|
||||
|
||||
Installation:
|
||||
|
||||
Do the following steps to add video_filter button to the CKEditor toolbar:
|
||||
|
||||
1. Open ckeditor.config.js (in the ckeditor module root)
|
||||
|
||||
2. Scroll down to the end of the file, right before "};" insert:
|
||||
|
||||
// Video_filter plugin.
|
||||
config.extraPlugins += (config.extraPlugins ? ',video_filter' : 'video_filter' );
|
||||
CKEDITOR.plugins.addExternal('video_filter', Drupal.settings.basePath + Drupal.settings.video_filter.modulepath + '/editors/ckeditor/');
|
||||
|
||||
3. Add button to the toolbar.
|
||||
|
||||
3.1 Go to Configuration -> CKEditor (admin/config/content/ckeditor)
|
||||
Click "Edit" on the profile you what to use with Linkit.
|
||||
|
||||
3.2 Expand "Editor appearance" and go to "Toolbar".
|
||||
|
||||
The button name is: video_filter
|
||||
For example if you have a toolbar with an array of buttons defined as
|
||||
follows:
|
||||
|
||||
['Bold','Italic']
|
||||
|
||||
simply add the button somewhere in the array:
|
||||
|
||||
['Bold','Italic','video_filter']
|
||||
|
||||
(remember the single quotes).
|
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file Plugin for inserting video tags with video_filter
|
||||
*/
|
||||
(function ($) {
|
||||
CKEDITOR.plugins.add('video_filter', {
|
||||
|
||||
requires : [],
|
||||
|
||||
init: function(editor) {
|
||||
|
||||
// Add Button
|
||||
editor.ui.addButton('video_filter', {
|
||||
label: 'Video filter',
|
||||
command: 'video_filter',
|
||||
icon: this.path + 'video_filter.png'
|
||||
});
|
||||
// Add Command
|
||||
editor.addCommand('video_filter', {
|
||||
exec : function () {
|
||||
var path = (Drupal.settings.video_filter.url.wysiwyg_ckeditor) ? Drupal.settings.video_filter.url.wysiwyg_ckeditor : Drupal.settings.video_filter.url.ckeditor
|
||||
var media = window.showModalDialog(path, { 'opener' : window, 'editorname' : editor.name }, "dialogWidth:580px; dialogHeight:480px; center:yes; resizable:yes; help:no;");
|
||||
}
|
||||
});
|
||||
|
||||
// Register an extra fucntion, this will be used in the popup.
|
||||
editor._.video_filterFnNum = CKEDITOR.tools.addFunction(insert, editor);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function insert(params, editor) {
|
||||
var selection = editor.getSelection(),
|
||||
ranges = selection.getRanges(),
|
||||
range,
|
||||
textNode;
|
||||
|
||||
editor.fire('saveSnapshot');
|
||||
|
||||
var str = '[video:' + params.file_url;
|
||||
if (params.width) {
|
||||
str += ' width:' + params.width;
|
||||
}
|
||||
if (params.height) {
|
||||
str += ' height:' + params.height;
|
||||
}
|
||||
if (params.align) {
|
||||
str += ' align:' + params.align;
|
||||
}
|
||||
if (params.autoplay) {
|
||||
str += ' autoplay:' + params.autoplay;
|
||||
}
|
||||
str += ']';
|
||||
|
||||
for (var i = 0, len = ranges.length; i < len; i++) {
|
||||
range = ranges[i];
|
||||
range.deleteContents();
|
||||
|
||||
textNode = CKEDITOR.dom.element.createFromHtml(str);
|
||||
range.insertNode(textNode);
|
||||
}
|
||||
|
||||
range.moveToPosition(textNode, CKEDITOR.POSITION_AFTER_END);
|
||||
range.select();
|
||||
|
||||
editor.fire('saveSnapshot');
|
||||
}
|
||||
|
||||
})(jQuery);
|
Binary file not shown.
After Width: | Height: | Size: 800 B |
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @file video_filter ckeditor dialog helper
|
||||
*/
|
||||
|
||||
var video_filter_dialog = {};
|
||||
(function ($) {
|
||||
video_filter_dialog = {
|
||||
init : function() {
|
||||
//Get CKEDITOR
|
||||
CKEDITOR = dialogArguments.opener.CKEDITOR;
|
||||
//Get the current instance name
|
||||
var name = dialogArguments.editorname;
|
||||
//Get the editor instance
|
||||
editor = CKEDITOR.instances[name];
|
||||
},
|
||||
|
||||
insert : function() {
|
||||
// Get the params from the form
|
||||
var params = this._getParams();
|
||||
//If no file url, just close this window
|
||||
if(params.file_url == "") {
|
||||
window.close();
|
||||
}
|
||||
else {
|
||||
CKEDITOR.tools.callFunction(editor._.video_filterFnNum, params, editor);
|
||||
window.close();
|
||||
}
|
||||
},
|
||||
|
||||
_getParams : function () {
|
||||
var params = {};
|
||||
$('fieldset:first-child input, fieldset:first-child select').each(function() {
|
||||
if($(this).attr('type') == "checkbox") {
|
||||
if($(this).is(':checked')) {
|
||||
params[$(this).attr('name')] = $(this).val();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if($(this).val() != "" && $(this).val() != "none") {
|
||||
params[$(this).attr('name')] = $(this).val();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return params;
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
var CKEDITOR, editor;
|
||||
|
||||
video_filter_dialog.init();
|
||||
|
||||
$('#edit-insert').click(function() {
|
||||
video_filter_dialog.insert();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#edit-cancel').click(function() {
|
||||
window.close();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery);
|
@@ -0,0 +1,9 @@
|
||||
var path = Drupal.settings.video_filter.url.wysiwyg_fckeditor;
|
||||
var basePath = Drupal.settings.basePath;
|
||||
var modulePath = Drupal.settings.video_filter.modulepath;
|
||||
|
||||
FCKCommands.RegisterCommand('video_filter', new FCKDialogCommand('video_filter', ' ', path, 580, 480));
|
||||
|
||||
var oVideoFilterItem = new FCKToolbarButton('video_filter', 'video_filter');
|
||||
oVideoFilterItem.IconPath = basePath + modulePath + '/editors/fckeditor/video_filter/video_filter.png';
|
||||
FCKToolbarItems.RegisterItem('video_filter', oVideoFilterItem);
|
Binary file not shown.
After Width: | Height: | Size: 800 B |
@@ -0,0 +1,53 @@
|
||||
var dialog = window.parent;
|
||||
var oEditor = dialog.InnerDialogLoaded();
|
||||
var FCK = oEditor.FCK;
|
||||
|
||||
dialog.SetAutoSize(true);
|
||||
|
||||
// Activate the "OK" button.
|
||||
dialog.SetOkButton(true);
|
||||
|
||||
(function ($) {
|
||||
$(document).ready(function() {
|
||||
$('#edit-cancel, #edit-insert').hide();
|
||||
$('*', document).keydown(function(ev) {
|
||||
if (ev.keyCode == 13) {
|
||||
// Prevent browsers from firing the click event on the first submit
|
||||
// button when enter is used to select.
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
// The "OK" button was hit.
|
||||
function Ok() {
|
||||
var sInnerHtml;
|
||||
(function ($) {
|
||||
var file_url = $('#edit-file-url').val();
|
||||
|
||||
if(file_url == "") {
|
||||
dialog.Cancel();
|
||||
}
|
||||
|
||||
var str = '[video:' + file_url;
|
||||
if ($('#edit-width').val() !== '') {
|
||||
str += ' width:' + $('#edit-width').val();
|
||||
}
|
||||
if ($('#edit-height').val() !== '') {
|
||||
str += ' height:' + $('#edit-height').val();
|
||||
}
|
||||
if ($('#edit-align').val() !== 'none') {
|
||||
str += ' align:' + $('#edit-align').val();
|
||||
}
|
||||
if ($('#edit-autoplay').is(':checked')) {
|
||||
str += ' autoplay:' + $('#edit-autoplay').val();
|
||||
}
|
||||
str += ']';
|
||||
|
||||
oEditor.FCKUndo.SaveUndoStep();
|
||||
|
||||
var text = oEditor.FCK.InsertHtml(str);
|
||||
})(jQuery);
|
||||
return true;
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
(function ($) {
|
||||
tinymce.create('tinymce.plugins.video_filter', {
|
||||
init : function(ed, url) {
|
||||
// Register commands
|
||||
ed.addCommand('mceVideoFilter', function() {
|
||||
ed.windowManager.open({
|
||||
file : Drupal.settings.video_filter.url.wysiwyg_tinymce,
|
||||
width : 580,
|
||||
height : 480,
|
||||
inline : true,
|
||||
scrollbars : 1,
|
||||
popup_css : false
|
||||
}, {
|
||||
plugin_url : url
|
||||
});
|
||||
});
|
||||
|
||||
// Register buttons
|
||||
ed.addButton('video_filter', {
|
||||
title : 'Video filter',
|
||||
cmd : 'mceVideoFilter',
|
||||
image : url + '/images/video_filter.png'
|
||||
});
|
||||
},
|
||||
|
||||
getInfo : function() {
|
||||
return {
|
||||
longname : 'Video Filter',
|
||||
author : 'Video Filter',
|
||||
authorurl : 'http://drupal.org/project/video_filter',
|
||||
infourl : 'http://drupal.org/project/video_filter',
|
||||
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||
};
|
||||
}
|
||||
});
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('video_filter', tinymce.plugins.video_filter);
|
||||
})(jQuery);
|
Binary file not shown.
After Width: | Height: | Size: 800 B |
@@ -0,0 +1,59 @@
|
||||
var video_filter_dialog = {};
|
||||
|
||||
(function ($) {
|
||||
video_filter_dialog = {
|
||||
insert : function() {
|
||||
var ed = tinyMCEPopup.editor, e;
|
||||
|
||||
tinyMCEPopup.restoreSelection();
|
||||
|
||||
tinyMCEPopup.execCommand("mceBeginUndoLevel");
|
||||
|
||||
var file_url = $('#edit-file-url').val();
|
||||
|
||||
// @Todo: validate width and hight is INTs?
|
||||
|
||||
if (file_url == "") {
|
||||
// File url is empty, we have nothing to insert, close the window
|
||||
ed.execCommand('mceRepaint');
|
||||
tinyMCEPopup.execCommand("mceEndUndoLevel");
|
||||
tinyMCEPopup.close();
|
||||
}
|
||||
else {
|
||||
var str = '[video:' + file_url;
|
||||
// If field is present (ie. not unset by the admin theme) and if value is not empty: insert value.
|
||||
if (typeof $('#edit-width').val() != 'undefined' && $('#edit-width').val() !== '') {
|
||||
str += ' width:' + $('#edit-width').val();
|
||||
}
|
||||
if (typeof $('#edit-height').val() != 'undefined' && $('#edit-height').val() !== '') {
|
||||
str += ' height:' + $('#edit-height').val();
|
||||
}
|
||||
if (typeof $('#edit-align').val() != 'undefined' && $('#edit-align').val() !== 'none') {
|
||||
str += ' align:' + $('#edit-align').val();
|
||||
}
|
||||
if ($('#edit-autoplay').is(':checked')) {
|
||||
str += ' autoplay:' + $('#edit-autoplay').val();
|
||||
}
|
||||
str += ']';
|
||||
|
||||
ed.execCommand('mceInsertContent', false, str);
|
||||
}
|
||||
|
||||
tinyMCEPopup.execCommand("mceEndUndoLevel");
|
||||
tinyMCEPopup.close();
|
||||
}
|
||||
};
|
||||
|
||||
Drupal.behaviors.video_filter_tinymce = {
|
||||
attach: function(context, settings) {
|
||||
$('#edit-insert').click(function() {
|
||||
video_filter_dialog.insert();
|
||||
});
|
||||
|
||||
$('#edit-cancel').click(function() {
|
||||
tinyMCEPopup.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery);
|
@@ -0,0 +1,613 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file contains all codecs provided by Video Filter.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_codec_info().
|
||||
*/
|
||||
function video_filter_codec_info() {
|
||||
$codecs = array();
|
||||
|
||||
$codecs['archive'] = array(
|
||||
'name' => t('Archive.org'),
|
||||
'sample_url' => 'http://www.archive.org/details/DrupalconBoston2008-TheStateOfDrupal',
|
||||
'callback' => 'video_filter_archive',
|
||||
'html5_callback' => 'video_filter_archive',
|
||||
'regexp' => '/archive\.org\/details\/([\w-_]+)/i',
|
||||
'ratio' => 4 / 3,
|
||||
);
|
||||
|
||||
$codecs['bliptv'] = array(
|
||||
'name' => t('Blip.tv'),
|
||||
'sample_url' => 'http://blip.tv/file/123456',
|
||||
'callback' => 'video_filter_bliptv',
|
||||
'regexp' => array(
|
||||
'@blip\.tv/rss/flash/([^"\&\?/]+)@i',
|
||||
'@blip\.tv/file/view/([^"\&\?/]+)@i',
|
||||
'@blip\.tv/file/([^"\&\?/]+)@i',
|
||||
'@blip\.tv/play/([^"\&\?/]+)@i',
|
||||
),
|
||||
'ratio' => 16 / 9,
|
||||
'control_bar_height' => 30,
|
||||
);
|
||||
|
||||
$codecs['capped'] = array(
|
||||
'name' => t('Capped'),
|
||||
'sample_url' => 'http://capped.tv/playeralt.php?vid=some-title',
|
||||
'callback' => 'video_filter_capped',
|
||||
'regexp' => '/capped\.tv\/([a-zA-Z0-9\-_]+)/',
|
||||
'ratio' => 425 / 355,
|
||||
);
|
||||
|
||||
$codecs['collegehumor'] = array(
|
||||
'name' => t('College Humor'),
|
||||
'sample_url' => 'http://www.collegehumor.com/video:1234567890',
|
||||
'callback' => 'video_filter_collegehumor',
|
||||
'regexp' => '/collegehumor\.com\/video\:([0-9]+)/',
|
||||
'ratio' => 16 / 9,
|
||||
'control_bar_height' => 0,
|
||||
);
|
||||
|
||||
$codecs['dailymotion'] = array(
|
||||
'name' => t('DailyMotion'),
|
||||
'sample_url' => 'http://www.dailymotion.com/video/some_video_title',
|
||||
'callback' => 'video_filter_dailymotion',
|
||||
'regexp' => '/dailymotion\.com\/video\/([a-z0-9\-_]+)/i',
|
||||
'ratio' => 4 / 3,
|
||||
'control_bar_height' => 20,
|
||||
);
|
||||
|
||||
$codecs['flickr_slideshows'] = array(
|
||||
'name' => t('Flickr Slideshows'),
|
||||
'sample_url' => 'http://www.flickr.com/photos/username/sets/1234567890/show/',
|
||||
'callback' => 'video_filter_flickr_slideshows',
|
||||
'regexp' => '/flickr\.com\/photos\/([a-zA-Z0-9@_\-]+)\/sets\/([0-9]+)\/?[show]?\/?/i',
|
||||
'ratio' => 4 / 3,
|
||||
'control_bar_height' => 0,
|
||||
);
|
||||
|
||||
$codecs['flickr_video'] = array(
|
||||
'name' => t('Flickr Video'),
|
||||
'sample_url' => 'http://www.flickr.com/photos/hansnilsson/1234567890/',
|
||||
'callback' => 'video_filter_flickr_video',
|
||||
'regexp' => '/flickr\.com\/photos\/([a-zA-Z0-9@_\-]+)\/([0-9]+)/',
|
||||
'ratio' => 4 / 3,
|
||||
'control_bar_height' => 0,
|
||||
);
|
||||
|
||||
$codecs['gametrailers'] = array(
|
||||
'name' => t('Game Trailers'),
|
||||
'sample_url' => 'http://www.gametrailers.com/video/some-title/12345',
|
||||
'callback' => 'video_filter_gametrailers',
|
||||
'regexp' => array(
|
||||
'/gametrailers\.com\/player\/([0-9]+)/',
|
||||
'/gametrailers\.com\/video\/([a-z0-9\-_]+)\/([0-9]+)/',
|
||||
),
|
||||
'ratio' => 16 / 9,
|
||||
);
|
||||
|
||||
$codecs['gamevideos'] = array(
|
||||
'name' => t('Game Videos'),
|
||||
'sample_url' => 'http://gamevideos.1up.com/video/id/12345',
|
||||
'callback' => 'video_filter_gamevideos',
|
||||
'regexp' => '/gamevideos\.1up\.com\/video\/id\/([0-9]+)/',
|
||||
'ratio' => 500 / 319,
|
||||
);
|
||||
|
||||
$codecs['godtube'] = array(
|
||||
'name' => t('GodTube'),
|
||||
'sample_url' => 'http://www.godtube.com/watch/?v=123abc',
|
||||
'callback' => 'video_filter_godtube',
|
||||
'regexp' => '/godtube\.com\/watch\/\?v=([a-z0-9\-_]+)/i',
|
||||
'ratio' => 400 / 283,
|
||||
'control_bar_height' => 40,
|
||||
);
|
||||
|
||||
$codecs['google'] = array(
|
||||
'name' => t('Google Video'),
|
||||
'sample_url' => 'http://video.google.com/videoplay?docid=-uN1qUeId',
|
||||
'callback' => 'video_filter_google',
|
||||
'regexp' => '/video\.google\.[a-z]+\.?[a-z]+?\/videoplay\?docid=(\-?[0-9]+)/',
|
||||
'ratio' => 400 / 326,
|
||||
);
|
||||
|
||||
$codecs['metacafe'] = array(
|
||||
'name' => t('Meta Cafe'),
|
||||
'sample_url' => 'http://www.metacafe.com/watch/1234567890/some_title/',
|
||||
'callback' => 'video_filter_metacafe',
|
||||
'regexp' => '/metacafe\.com\/watch\/([a-z0-9\-_]+)\/([a-z0-9\-_]+)/i',
|
||||
'ratio' => 400 / 313,
|
||||
'control_bar_height' => 32,
|
||||
);
|
||||
|
||||
$codecs['myspace'] = array(
|
||||
'name' => t('MySpace'),
|
||||
'sample_url' => 'http://myspace.com/video/vid/1234567890',
|
||||
'callback' => 'video_filter_myspace',
|
||||
'regexp' => array(
|
||||
'/vids\.myspace\.com\/.*VideoID=([0-9]+)/i',
|
||||
'/myspace\.com\/video\/([a-z])+\/([0-9]+)/i',
|
||||
'/myspace\.com\/video\/([a-z0-9\-_]+)\/([a-z0-9\-_]+)\/([a-z0-9]+)/i',
|
||||
'/myspace\.com\/([a-z0-9\-_]+)\/videos\/([a-z0-9\-_]+)\/([a-z0-9]+)/i',
|
||||
),
|
||||
'ratio' => 620 / 400,
|
||||
'control_bar_height' => 40,
|
||||
);
|
||||
|
||||
$codecs['picasa_slideshows'] = array(
|
||||
'name' => t('Picasa Slideshows'),
|
||||
'sample_url' => 'http://picasaweb.google.com/data/feed/base/user/USER_NAME/albumid/5568104935784209834?alt=rss&kind=photo&hl=en_US',
|
||||
'callback' => 'video_filter_picasa_slideshows',
|
||||
'instructions' => t('You must use the URL of the RSS feed for the Picasa album:') .
|
||||
'<ol>' .
|
||||
'<li>' . t('View the album in Picasa (you should see thumbnails, not a slideshow).') . '</li>' .
|
||||
'<li>' . t('Find the "RSS" link and click it.') . '</li>' .
|
||||
'<li>' . t('Copy the resulting URL from the browser address bar. Example:') . '<br />' .
|
||||
'<code>[video: http://picasaweb.google.com/data/feed/base/user/USER_NAME/albumid/5568104935784209834?alt=rss&kind=photo&hl=en_US]</code>' .
|
||||
'</li>' .
|
||||
'</ol>',
|
||||
'regexp' => '/picasaweb\.google\.com\/data\/feed\/base\/user\/([a-zA-Z0-9@_\-\.]+)\/albumid\/([a-z0-9]+)/i',
|
||||
'ratio' => 800 / 600,
|
||||
);
|
||||
|
||||
$codecs['slideshare'] = array(
|
||||
'name' => t('Slideshare'),
|
||||
'sample_url' => 'http://slideshare.net/1759622',
|
||||
'callback' => 'video_filter_slideshare',
|
||||
'instructions' => t('You need to construct your own URL, using the "Wordpress Embed" code from Slideshare, extract the "id", and form the URL like this: slideshare.net/1759622'),
|
||||
'regexp' => array(
|
||||
'/slideshare\.net\/\?id=([a-z0-9]+)/',
|
||||
'/slideshare\.net\/([a-z0-9]+)/',
|
||||
),
|
||||
'ratio' => 425 / 355,
|
||||
);
|
||||
|
||||
$codecs['streamhoster'] = array(
|
||||
'name' => t('Streamhoster'),
|
||||
'sample_url' => 'http://web26.streamhoster.com/username/filename.flv',
|
||||
'callback' => 'video_filter_streamhoster',
|
||||
'regexp' => '/([a-z0-9]+)\:\/\/([a-z0-9\-_]+)\.streamhoster\.com\/([a-z0-9\-_]+)\/([a-z0-9\-_\.]+)/i',
|
||||
'ratio' => 480 / 360,
|
||||
);
|
||||
|
||||
$codecs['teachertube'] = array(
|
||||
'name' => t('Teachertube'),
|
||||
'sample_url' => 'http://www.teachertube.com/viewVideo.php?video_id=VIDEOID',
|
||||
'callback' => 'video_filter_teachertube',
|
||||
'regexp' => '/teachertube\.com\/viewVideo.php\?video_id\=([0-9]+)/i',
|
||||
'ratio' => 16 / 9,
|
||||
);
|
||||
|
||||
$codecs['vimeo'] = array(
|
||||
'name' => t('Vimeo'),
|
||||
'sample_url' => 'http://www.vimeo.com/123456',
|
||||
'callback' => 'video_filter_vimeo',
|
||||
'html5_callback' => 'video_filter_vimeo_html5',
|
||||
'regexp' => '/vimeo\.com\/([0-9]+)/',
|
||||
'ratio' => 16 / 9,
|
||||
'control_bar_height' => 0,
|
||||
);
|
||||
|
||||
$codecs['wistia'] = array(
|
||||
'name' => t('Wistia'),
|
||||
'sample_url' => 'http://wistia.com/medias/9pj9n6ftlk',
|
||||
'callback' => 'video_filter_wistia_html5',
|
||||
'html5_callback' => 'video_filter_wistia_html5',
|
||||
'regexp' => '@https?://(.+\.)?(wistia\.com|wi\.st)/((m|medias|projects)|embed/(iframe|playlists))/([a-zA-Z0-9]+)@',
|
||||
);
|
||||
|
||||
$codecs['youtube'] = array(
|
||||
'name' => t('YouTube'),
|
||||
'sample_url' => 'http://www.youtube.com/watch?v=uN1qUeId',
|
||||
'callback' => 'video_filter_youtube',
|
||||
'html5_callback' => 'video_filter_youtube_html5',
|
||||
'regexp' => array(
|
||||
'/youtube\.com\/watch\?v=([a-z0-9\-_]+)/i',
|
||||
'/youtu.be\/([a-z0-9\-_]+)/i',
|
||||
'/youtube\.com\/v\/([a-z0-9\-_]+)/i',
|
||||
),
|
||||
'ratio' => 16 / 9,
|
||||
'control_bar_height' => 25,
|
||||
);
|
||||
|
||||
$codecs['youtube_playlist'] = array(
|
||||
'name' => t('YouTube (Playlist)'),
|
||||
'sample_url' => 'http://www.youtube.com/playlist?list=uN1qUeId',
|
||||
'callback' => 'video_filter_youtube_playlist_html5',
|
||||
'regexp' => array(
|
||||
'/youtube\.com\/playlist\?list=([a-z0-9\-_]+)/i',
|
||||
),
|
||||
'ratio' => 16 / 9,
|
||||
'control_bar_height' => 25,
|
||||
);
|
||||
|
||||
return $codecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Archive.org codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_archive($video) {
|
||||
$video['source'] = 'http://www.archive.org/embed/' . $video['codec']['matches'][1];
|
||||
|
||||
return video_filter_iframe($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Blip.tv codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_bliptv($video) {
|
||||
$id = $video['codec']['matches'][1];
|
||||
|
||||
// Since video ID in URL is different than in embed code, use API to lookup
|
||||
// the embed code video ID. Adapted from emfield.module.
|
||||
$result = drupal_http_request('http://blip.tv/file/' . $id . '?skin=api');
|
||||
|
||||
if ($result->code == 200) {
|
||||
$parser = drupal_xml_parser_create($result->data);
|
||||
$vals = array();
|
||||
$index = array();
|
||||
xml_parse_into_struct($parser, $result->data, $vals, $index);
|
||||
xml_parser_free($parser);
|
||||
|
||||
$response = array();
|
||||
// @todo: What's $arghash supposed to be? It's undefined.
|
||||
$response['_emfield_arghash'] = $arghash;
|
||||
$level = array();
|
||||
$start_level = 1;
|
||||
foreach ($vals as $xml_elem) {
|
||||
if ($xml_elem['type'] == 'open') {
|
||||
if (array_key_exists('attributes', $xml_elem)) {
|
||||
list($level[$xml_elem['level']], $extra) = array_values($xml_elem['attributes']);
|
||||
}
|
||||
else {
|
||||
$level[$xml_elem['level']] = $xml_elem['tag'];
|
||||
}
|
||||
}
|
||||
if ($xml_elem['type'] == 'complete') {
|
||||
$php_stmt = '$response';
|
||||
while ($start_level < $xml_elem['level']) {
|
||||
$php_stmt .= '[$level[' . $start_level . ']]';
|
||||
$start_level++;
|
||||
}
|
||||
$php_stmt .= '[$xml_elem[\'tag\']][] = $xml_elem[\'value\'];' . $php_stmt . '[$xml_elem[\'tag\']][] = $xml_elem[\'attributes\'];';
|
||||
eval($php_stmt);
|
||||
$start_level--;
|
||||
}
|
||||
}
|
||||
$id = $response['EMBEDLOOKUP'][0];
|
||||
// Protect from XSS.
|
||||
if (preg_match("/[^A-Za-z0-9]/", $id, $matches)) {
|
||||
watchdog('Video Filter', 'A faulty Blip.tv ID has been detected.');
|
||||
$id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$video['source'] = 'http://blip.tv/play/' . $id;
|
||||
$params = array(
|
||||
'allowscriptaccess' => 'always',
|
||||
);
|
||||
|
||||
return video_filter_flash($video, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Capped codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_capped($video) {
|
||||
$video['source'] = 'http://capped.micksam7.com/playeralt.swf?vid=' . $video['codec']['matches'][1];
|
||||
|
||||
return video_filter_flash($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for College Humor codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_collegehumor($video) {
|
||||
$video['source'] = 'http://www.collegehumor.com/moogaloop/moogaloop.swf?clip_id=' . $video['codec']['matches'][1] . '&fullscreen=1';
|
||||
|
||||
return video_filter_flash($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for DailyMotion codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_dailymotion($video) {
|
||||
$video['source'] = 'http://www.dailymotion.com/swf/' . $video['codec']['matches'][1];
|
||||
|
||||
return video_filter_flash($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Flickr Slideshows codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_flickr_slideshows($video) {
|
||||
$slideshow_player_url = 'http://www.flickr.com/apps/slideshow/show.swf?v=67348';
|
||||
$video['source'] = $slideshow_player_url . ($video['autoplay'] ? '&autoplay=1' : '');
|
||||
|
||||
$user_name = $video['codec']['matches'][1];
|
||||
$set_id = $video['codec']['matches'][2];
|
||||
|
||||
$params['flashvars'] = "&offsite=true&lang=en-us&page_show_url=%2Fphotos%2F$user_name%2Fsets%2F$set_id%2Fshow%2F&page_show_back_url=%2Fphotos%2F$user_name%2Fsets%2F$set_id%2F&set_id=$set_id&jump_to=";
|
||||
|
||||
return video_filter_flash($video, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Flickr Video codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_flickr_video($video) {
|
||||
$video['source'] = 'http://www.flickr.com/apps/video/stewart.swf?v=1.161';
|
||||
|
||||
$params['flashvars'] = '&photo_id=' . $video['codec']['matches'][2] . '&flickr_show_info_box=true';
|
||||
|
||||
return video_filter_flash($video, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Game Trailers codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_gametrailers($video) {
|
||||
if (is_numeric($video['codec']['matches'][1])) {
|
||||
$match = $video['codec']['matches'][1];
|
||||
}
|
||||
elseif (is_numeric($video['codec']['matches'][2])) {
|
||||
$match = $video['codec']['matches'][2];
|
||||
}
|
||||
$video['source'] = 'http://media.mtvnservices.com/embed/mgid:moses:video:gametrailers.com:' . $match;
|
||||
|
||||
return video_filter_iframe($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Game Videos codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_gamevideos($video) {
|
||||
$video['source'] = 'http://gamevideos.1up.com/swf/gamevideos12.swf?embedded=1&fullscreen=1&autoplay=0&src=http://gamevideos.1up.com/do/videoListXML%3Fid%3D' . $video['codec']['matches'][1];
|
||||
|
||||
return video_filter_flash($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for GodTube codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_godtube($video) {
|
||||
$video['source'] = 'http://www.godtube.com/embed/watch/' . $video['codec']['matches'][1];
|
||||
|
||||
return video_filter_iframe($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Google Video codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_google($video) {
|
||||
$video['source'] = 'http://video.google.com/googleplayer.swf?docId=' . $video['codec']['matches'][1];
|
||||
|
||||
return video_filter_flash($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Meta Cafe codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_metacafe($video) {
|
||||
$video['source'] = 'http://metacafe.com/fplayer/' . $video['codec']['matches'][1] . '/' . $video['codec']['matches'][2] . '.swf';
|
||||
|
||||
return video_filter_flash($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for MySpace codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_myspace($video) {
|
||||
// The last match is the ID we need.
|
||||
$last = count($video['codec']['matches']);
|
||||
$video['source'] = 'http://mediaservices.myspace.com/services/media/embed.aspx/m=' . $video['codec']['matches'][$last - 1];
|
||||
|
||||
return video_filter_flash($video, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Picasa Slideshows codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_picasa_slideshows($video) {
|
||||
$video['source'] = 'http://picasaweb.google.com/s/c/bin/slideshow.swf';
|
||||
|
||||
$user_name = $video['codec']['matches'][1];
|
||||
$set_id = $video['codec']['matches'][2];
|
||||
|
||||
$params['flashvars'] = "host=picasaweb.google.com&&feat=flashalbum&RGB=0x000000&feed=http%3A%2F%2Fpicasaweb.google.com%2Fdata%2Ffeed%2Fapi%2Fuser%2F" . $user_name . "%2Falbumid%2F" . $set_id . "%3Falt%3Drss%26kind%3Dphoto%26" . ($video['autoplay'] ? '' : '&noautoplay=1');
|
||||
|
||||
return video_filter_flash($video, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Slideshare codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_slideshare($video) {
|
||||
$video['source'] = 'http://www.slideshare.net/slideshow/embed_code/' . $video['codec']['matches'][1];
|
||||
|
||||
return video_filter_iframe($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Streamhoster codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_streamhoster($video) {
|
||||
$video['source'] = 'http://public.streamhoster.com/Resources/Flash/JWFLVMediaPlayer/mediaplayer.swf';
|
||||
|
||||
$params = array('allowscriptaccess' => 'always');
|
||||
$protocol = $video['codec']['matches'][1];
|
||||
|
||||
if ($protocol == 'rtmp') {
|
||||
$params['flashvars'] = 'file=' . urlencode('/' . $video['codec']['matches'][4]);
|
||||
$params['flashvars'] .= '&streamer=' . urlencode('rtmp://' . $video['codec']['matches'][2] . '.streamhoster.com/' . $video['codec']['matches'][3]);
|
||||
$params['flashvars'] .= '&type=rtmp';
|
||||
}
|
||||
elseif ($protocol == 'http') {
|
||||
$params['flashvars'] = 'file=' . urlencode('http://' . $video['codec']['matches'][2] . '.streamhoster.com/' . $video['codec']['matches'][3] . '/' . $video['codec']['matches'][4]);
|
||||
$params['flashvars'] .= '&type=video';
|
||||
}
|
||||
|
||||
$params['flashvars'] .= '&width=' . $video['width'];
|
||||
$params['flashvars'] .= '&height=' . $video['height'];
|
||||
$params['flashvars'] .= !empty($video['autoplay']) ? '&autostart=true' : '&autostart=false';
|
||||
|
||||
return video_filter_flash($video, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Teachertube codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_teachertube($video) {
|
||||
$video['source'] = 'http://www.teachertube.com/embed/player.swf';
|
||||
|
||||
$params['flashvars'] = 'file=http://www.teachertube.com/embedFLV.php?pg=video_' . $video['codec']['matches'][1] . '&menu=false&frontcolor=ffffff&lightcolor=FF0000&logo=http://www.teachertube.com/www3/images/greylogo.swf&skin=http://www.teachertube.com/embed/overlay.swf&volume=80&controlbar=over&displayclick=link&viral.link=http://www.teachertube.com/viewVideo.php?video_id=' . $video['codec']['matches'][1] . '&stretching=exactfit&plugins=viral-2&viral.callout=none&viral.onpause=false';
|
||||
|
||||
return video_filter_flash($video, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Vimeo codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_vimeo($video) {
|
||||
$video['source'] = 'http://www.vimeo.com/moogaloop.swf?clip_id=' . $video['codec']['matches'][1] . '&server=www.vimeo.com&fullscreen=1&show_title=1&show_byline=1&show_portrait=0&color=&autoplay=' . $video['autoplay'];
|
||||
|
||||
return video_filter_flash($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 callback for Vimeo codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_vimeo_html5($video) {
|
||||
$video['source'] = 'http://player.vimeo.com/video/' . $video['codec']['matches'][1] . ($video['autoplay'] ? '?autoplay=1' : '');
|
||||
|
||||
return video_filter_iframe($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for YouTube codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_youtube($video) {
|
||||
$attributes = array(
|
||||
'rel' => $video['related'] ? 'rel=1' : 'rel=0',
|
||||
'autoplay' => $video['autoplay'] ? 'autoplay=1' : 'autoplay=0',
|
||||
'fs' => 'fs=1',
|
||||
);
|
||||
|
||||
$video['source'] = 'http://www.youtube.com/v/' . $video['codec']['matches'][1] . '?' . implode('&', $attributes);
|
||||
|
||||
$params['wmode'] = 'opaque';
|
||||
|
||||
return video_filter_flash($video, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 callback for YouTube codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_youtube_html5($video) {
|
||||
$attributes = array(
|
||||
'rel' => $video['related'] ? 'rel=1' : 'rel=0',
|
||||
'autoplay' => $video['autoplay'] ? 'autoplay=1' : 'autoplay=0',
|
||||
'wmode' => 'wmode=opaque',
|
||||
);
|
||||
$video['source'] = 'http://www.youtube.com/embed/' . $video['codec']['matches'][1] . '?' . implode('&', $attributes);
|
||||
|
||||
return video_filter_iframe($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML5 callback for YouTube (Playlist) codec.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_youtube_playlist_html5($video) {
|
||||
$attributes = array(
|
||||
'rel' => $video['related'] ? 'rel=1' : 'rel=0',
|
||||
'autoplay' => $video['autoplay'] ? 'autoplay=1' : 'autoplay=0',
|
||||
'wmode' => 'wmode=opaque',
|
||||
);
|
||||
$video['source'] = 'http://www.youtube.com/embed/videoseries?list=' . $video['codec']['matches'][1] . '&' . implode('&', $attributes);
|
||||
|
||||
return video_filter_iframe($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Wistia codec.
|
||||
*
|
||||
* Adapted from the media_wistia module.
|
||||
*
|
||||
* @see video_filter_codec_info()
|
||||
*/
|
||||
function video_filter_wistia_html5($video) {
|
||||
$video_code = $video['codec']['matches'][6];
|
||||
$matches = $video['codec']['matches'];
|
||||
$embed_type = ($matches[3] == 'projects' || $matches[5] == 'playlists') ? 'playlists' : 'iframe';
|
||||
|
||||
// Get embed code via oEmbed.
|
||||
$endpoint = 'http://fast.wistia.com/oembed';
|
||||
$options = array(
|
||||
'url' => "http://fast.wistia.com/embed/{$embed_type}/{$video_code}",
|
||||
'width' => $video['width'],
|
||||
'height' => $video['height'],
|
||||
);
|
||||
$data = video_filter_oembed_request($endpoint, $options);
|
||||
$html = $data['html'];
|
||||
|
||||
// See if the video source is already an iframe src.
|
||||
$pattern = '@https?://fast.wistia.com/embed/(iframe|playlists)/[a-zA-Z0-9]+\?+.+@';
|
||||
$matches = array();
|
||||
if (preg_match($pattern, $video['source'], $matches)) {
|
||||
// Replace the oEmbed iframe src with that provided in the token, in order
|
||||
// to support embed builder URLs.
|
||||
$pattern = '@https?://fast.wistia.com/embed/(iframe|playlists)/[a-zA-Z0-9]+\?[^"]+@';
|
||||
$replacement = $matches[0];
|
||||
$html = preg_replace($pattern, $replacement, $html);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Default alignment styles
|
||||
*/
|
||||
.video-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.video-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.video-center {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
name = Video Filter
|
||||
description = Filter to include videos from Youtube, Google video etc
|
||||
core = 7.x
|
||||
package = Input filters
|
||||
|
||||
stylesheets[all][] = video_filter.css
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-14
|
||||
version = "7.x-3.1"
|
||||
core = "7.x"
|
||||
project = "video_filter"
|
||||
datestamp = "1352915891"
|
||||
|
@@ -0,0 +1,644 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Video filter is a highly flexible and easy extendable filter module to embed
|
||||
* any type of video in your site using a simple tag.
|
||||
*/
|
||||
|
||||
module_load_include('inc', 'video_filter', 'video_filter.codecs');
|
||||
|
||||
/**
|
||||
* Implements hook_filter_info().
|
||||
*/
|
||||
function video_filter_filter_info() {
|
||||
$filters = array();
|
||||
$filters['video_filter'] = array(
|
||||
'title' => t('Video Filter'),
|
||||
'description' => t('Substitutes [video:URL] with embedded HTML.'),
|
||||
'process callback' => '_video_filter_process',
|
||||
'settings callback' => '_video_filter_settings',
|
||||
'default settings' => array(
|
||||
'video_filter_width' => '400',
|
||||
'video_filter_height' => '400',
|
||||
'video_filter_autoplay' => 1,
|
||||
'video_filter_related' => 1,
|
||||
'video_filter_html5' => 1,
|
||||
),
|
||||
'tips callback' => '_video_filter_tips',
|
||||
// See http://drupal.org/node/1061244.
|
||||
'weight' => -1,
|
||||
);
|
||||
return $filters;
|
||||
}
|
||||
|
||||
function _video_filter_settings($form, &$form_state, $filter, $format, $defaults, $filters) {
|
||||
|
||||
$settings['video_filter_width'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Default width setting'),
|
||||
'#default_value' => isset($filter->settings['video_filter_width']) ? $filter->settings['video_filter_width'] : $defaults['video_filter_width'],
|
||||
'#maxlength' => 4,
|
||||
);
|
||||
$settings['video_filter_height'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Default height setting'),
|
||||
'#default_value' => isset($filter->settings['video_filter_height']) ? $filter->settings['video_filter_height'] : $defaults['video_filter_height'],
|
||||
'#maxlength' => 4,
|
||||
);
|
||||
$settings['video_filter_autoplay'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Default autoplay setting'),
|
||||
'#description' => t('Not all video formats support this setting.'),
|
||||
'#default_value' => isset($filter->settings['video_filter_autoplay']) ? $filter->settings['video_filter_autoplay'] : $defaults['video_filter_autoplay'],
|
||||
'#options' => array(
|
||||
0 => t('No'),
|
||||
1 => t('Yes'),
|
||||
),
|
||||
);
|
||||
$settings['video_filter_related'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Related videos setting'),
|
||||
'#description' => t('Show "related videos"? Not all video formats support this setting.'),
|
||||
'#default_value' => isset($filter->settings['video_filter_related']) ? $filter->settings['video_filter_related'] : $defaults['video_filter_related'],
|
||||
'#options' => array(
|
||||
0 => t('No'),
|
||||
1 => t('Yes'),
|
||||
),
|
||||
);
|
||||
|
||||
$settings['video_filter_html5'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Use HTML5'),
|
||||
'#description' => t('Use HTML5 if the codec provides it. Makes your videos more device agnostic.'),
|
||||
'#default_value' => isset($filter->settings['video_filter_html5']) ? $filter->settings['video_filter_html5'] : $defaults['video_filter_html5'],
|
||||
'#options' => array(
|
||||
0 => t('No'),
|
||||
1 => t('Yes'),
|
||||
),
|
||||
);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
function _video_filter_tips($filter, $format, $long = FALSE) {
|
||||
if ($long) {
|
||||
$codecs = video_filter_get_codec_info();
|
||||
$supported = array();
|
||||
$instructions = array();
|
||||
foreach ($codecs as $codec) {
|
||||
$supported[] = $codec['name'];
|
||||
$instructions[] = isset($codec['instructions']) ? '<li>' . $codec['name'] . ':<br/>' . $codec['instructions'] . '</li>' : '';
|
||||
}
|
||||
return t('
|
||||
<p><strong>Video Filter</strong></p>
|
||||
<p>You may insert videos from popular video sites by using a simple tag <code>[video:URL]</code>.</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>Single video:<br /><code>[video:http://www.youtube.com/watch?v=uN1qUeId]</code></li>
|
||||
<li>Random video out of multiple:<br /><code>[video:http://www.youtube.com/watch?v=uN1qUeId1,http://www.youtube.com/watch?v=uN1qUeId2]</code></li>
|
||||
<li>Override default autoplay setting: <code>[video:http://www.youtube.com/watch?v=uN1qUeId autoplay:1]</code></li>
|
||||
<li>Override default width and height:<br /><code>[video:http://www.youtube.com/watch?v=uN1qUeId width:X height:Y]</code></li>
|
||||
<li>Override default aspect ratio:<br /><code>[video:http://www.youtube.com/watch?v=uN1qUeId ratio:4/3]</code></li>
|
||||
<li>Align the video:<br /><code>[video:http://www.youtube.com/watch?v=uN1qUeId align:right]</code></li>
|
||||
</ul>
|
||||
<p>Supported sites: @codecs.</p>
|
||||
<p>Special instructions:</p>
|
||||
<small>Some codecs need special input. You\'ll find those instructions here.</small>
|
||||
<ul>!instructions</ul>', array(
|
||||
'@codecs' => implode(', ', $supported),
|
||||
'!instructions' => implode('', $instructions),
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
return t('You may insert videos with [video:URL]');
|
||||
}
|
||||
}
|
||||
|
||||
function _video_filter_process($text, $filter, $format, $langcode, $cache, $cache_id) {
|
||||
if (preg_match_all('/\[video(\:(.+))?( .+)?\]/isU', $text, $matches_code)) {
|
||||
foreach ($matches_code[0] as $ci => $code) {
|
||||
$video = array(
|
||||
'source' => $matches_code[2][$ci],
|
||||
'autoplay' => $filter->settings['video_filter_autoplay'],
|
||||
'related' => $filter->settings['video_filter_related'],
|
||||
);
|
||||
|
||||
// Pick random out of multiple sources separated by comma (,).
|
||||
if (strstr($video['source'], ',')) {
|
||||
$sources = explode(',', $video['source']);
|
||||
$random = array_rand($sources, 1);
|
||||
$video['source'] = $sources[$random];
|
||||
}
|
||||
|
||||
// Load all codecs.
|
||||
$codecs = video_filter_get_codec_info();
|
||||
|
||||
// Find codec.
|
||||
foreach ($codecs as $codec_name => $codec) {
|
||||
if (!is_array($codec['regexp'])) {
|
||||
$codec['regexp'] = array($codec['regexp']);
|
||||
}
|
||||
|
||||
// Try different regular expressions.
|
||||
foreach ($codec['regexp'] as $delta => $regexp) {
|
||||
if (preg_match($regexp, $video['source'], $matches)) {
|
||||
$video['codec'] = $codec;
|
||||
$video['codec']['delta'] = $delta;
|
||||
$video['codec']['matches'] = $matches;
|
||||
// Used in theme function:
|
||||
$video['codec']['codec_name'] = $codec_name;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Codec found.
|
||||
if (isset($video['codec'])) {
|
||||
// Override default attributes.
|
||||
if ($matches_code[3][$ci] && preg_match_all('/\s+([a-zA-Z_]+)\:(\s+)?([0-9a-zA-Z\/]+)/i', $matches_code[3][$ci], $matches_attributes)) {
|
||||
foreach ($matches_attributes[0] as $ai => $attribute) {
|
||||
$video[$matches_attributes[1][$ai]] = $matches_attributes[3][$ai];
|
||||
}
|
||||
}
|
||||
|
||||
// Use configured ratio if present, otherwise use that from the codec,
|
||||
// if set. Fall back to 1.
|
||||
$ratio = 1;
|
||||
if (isset($video['ratio']) && preg_match('/(\d+)\/(\d+)/', $video['ratio'], $tratio)) {
|
||||
// Validate given ratio parameter.
|
||||
$ratio = $tratio[1] / $tratio[2];
|
||||
}
|
||||
elseif (isset($video['codec']['ratio'])) {
|
||||
$ratio = $video['codec']['ratio'];
|
||||
}
|
||||
|
||||
// Sets video width & height after any user input has been parsed.
|
||||
// First, check if user has set a width.
|
||||
if (isset($video['width']) && !isset($video['height'])) {
|
||||
$video['height'] = $filter->settings['video_filter_height'];
|
||||
}
|
||||
// Else, if user has set height.
|
||||
elseif (isset($video['height']) && !isset($video['width'])) {
|
||||
$video['width'] = $video['height'] * $ratio;
|
||||
}
|
||||
// Maybe both?
|
||||
elseif (isset($video['height']) && isset($video['width'])) {
|
||||
$video['width'] = $video['width'];
|
||||
$video['height'] = $video['height'];
|
||||
}
|
||||
// Fall back to defaults.
|
||||
elseif (!isset($video['height']) && !isset($video['width'])) {
|
||||
$video['width'] = $filter->settings['video_filter_width'] != '' ? $filter->settings['video_filter_width'] : 400;
|
||||
$video['height'] = $filter->settings['video_filter_height'] != '' ? $filter->settings['video_filter_height'] : 400;
|
||||
}
|
||||
|
||||
// Default value for control bar height.
|
||||
$control_bar_height = 0;
|
||||
if (isset($video['control_bar_height'])) {
|
||||
// Respect control_bar_height option if present.
|
||||
$control_bar_height = $video['control_bar_height'];
|
||||
}
|
||||
elseif (isset($video['codec']['control_bar_height'])) {
|
||||
// Respect setting provided by codec otherwise.
|
||||
$control_bar_height = $video['codec']['control_bar_height'];
|
||||
}
|
||||
|
||||
// Resize to fit within width and height repecting aspect ratio.
|
||||
if ($ratio) {
|
||||
$scale_factor = min(array(
|
||||
($video['height'] - $control_bar_height),
|
||||
$video['width'] / $ratio,
|
||||
));
|
||||
$video['height'] = round($scale_factor + $control_bar_height);
|
||||
$video['width'] = round($scale_factor * $ratio);
|
||||
}
|
||||
|
||||
$video['autoplay'] = (bool) $video['autoplay'];
|
||||
$video['align'] = (isset($video['align']) && in_array($video['align'], array(
|
||||
'left',
|
||||
'right',
|
||||
'center',
|
||||
))) ? $video['align'] : NULL;
|
||||
|
||||
// Let modules have final say on video parameters.
|
||||
drupal_alter('video_filter_video', $video);
|
||||
|
||||
if (isset($video['codec']['html5_callback']) && $filter->settings['video_filter_html5'] == 1 && is_callable($video['codec']['html5_callback'], FALSE)) {
|
||||
$replacement = call_user_func($video['codec']['html5_callback'], $video);
|
||||
}
|
||||
elseif (is_callable($video['codec']['callback'], FALSE)) {
|
||||
$replacement = call_user_func($video['codec']['callback'], $video);
|
||||
}
|
||||
else {
|
||||
// Invalid callback.
|
||||
$replacement = '<!-- VIDEO FILTER - INVALID CALLBACK IN: ' . $pattern . ' -->';
|
||||
}
|
||||
}
|
||||
// Invalid format.
|
||||
else {
|
||||
$replacement = '<!-- VIDEO FILTER - INVALID CODEC IN: ' . $code . ' -->';
|
||||
}
|
||||
|
||||
$text = str_replace($code, $replacement, $text);
|
||||
}
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that calls the theme function.
|
||||
*/
|
||||
function video_filter_flash($video, $params = array()) {
|
||||
return theme('video_filter_flash', array('video' => $video, 'params' => $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that calls the theme function.
|
||||
*/
|
||||
function video_filter_iframe($video) {
|
||||
return theme('video_filter_iframe', array('video' => $video));
|
||||
}
|
||||
|
||||
function video_filter_get_codec_info() {
|
||||
static $codecs;
|
||||
if (!isset($codecs)) {
|
||||
$codecs = module_invoke_all('codec_info');
|
||||
drupal_alter('video_filter_codec_info', $codecs);
|
||||
}
|
||||
return $codecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that outputs the <object> element.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_video_filter_flash($variables) {
|
||||
$output = '';
|
||||
|
||||
$video = $variables['video'];
|
||||
$params = isset($variables['params']) ? $variables['params'] : array();
|
||||
|
||||
$classes = video_filter_get_classes($video);
|
||||
|
||||
$output .= '<object class="' . implode(' ', $classes) . '" type="application/x-shockwave-flash" ';
|
||||
|
||||
$output .= 'width="' . $video['width'] . '" height="' . $video['height'] . '" data="' . $video['source'] . '">' . "\n";
|
||||
|
||||
$defaults = array(
|
||||
'movie' => $video['source'],
|
||||
'wmode' => 'transparent',
|
||||
'allowFullScreen' => 'true',
|
||||
);
|
||||
|
||||
$params = array_merge($defaults, (is_array($params) && count($params)) ? $params : array());
|
||||
|
||||
foreach ($params as $name => $value) {
|
||||
$output .= ' <param name="' . $name . '" value="' . $value . '" />' . "\n";
|
||||
}
|
||||
|
||||
$output .= '</object>' . "\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that outputs HTML5 compatible iFrame for codecs that support it.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_video_filter_iframe($variables) {
|
||||
$video = $variables['video'];
|
||||
|
||||
$classes = video_filter_get_classes($video);
|
||||
|
||||
$output = '<iframe src="' . $video['source'] . '" width="' . $video['width'] . '" height="' . $video['height'] . '" class="' . implode(' ', $classes) . '" frameborder="0"></iframe>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function video_filter_theme($existing, $type, $theme, $path) {
|
||||
return array(
|
||||
'video_filter_flash' => array(
|
||||
'variables' => array('video' => NULL, 'params' => NULL),
|
||||
),
|
||||
'video_filter_iframe' => array(
|
||||
'variables' => array('video' => NULL, 'params' => NULL),
|
||||
),
|
||||
'video_filter_dashboard' => array(
|
||||
'variables' => array('form' => NULL),
|
||||
'template' => 'video_filter_dashboard',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that extracts some classes from $video.
|
||||
*/
|
||||
function video_filter_get_classes($video) {
|
||||
$classes = array(
|
||||
'video-filter',
|
||||
// Add codec name.
|
||||
'video-' . $video['codec']['codec_name'],
|
||||
);
|
||||
|
||||
// Add alignment.
|
||||
if (isset($video['align'])) {
|
||||
$classes[] = 'video-' . $video['align'];
|
||||
}
|
||||
|
||||
// First match is the URL, we don't want that as a class.
|
||||
unset($video['codec']['matches'][0]);
|
||||
foreach ($video['codec']['matches'] as $match) {
|
||||
$classes[] = 'vf-' . strtolower(preg_replace('/[^a-zA-Z0-9]/', '', $match));
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function video_filter_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['video_filter/dashboard/%'] = array(
|
||||
'title' => 'Videofilter',
|
||||
'description' => 'Dashboard',
|
||||
'page callback' => 'video_filter_dashboard_page',
|
||||
'access arguments' => array('access content'),
|
||||
'type' => MENU_CALLBACK,
|
||||
'page arguments' => array(2),
|
||||
'theme callback' => '_video_filter_dashboard_theme',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the theme name to be used when showing linkit dashboard
|
||||
*/
|
||||
function _video_filter_dashboard_theme() {
|
||||
return variable_get('admin_theme', 'seven');
|
||||
}
|
||||
|
||||
/**
|
||||
* Template preprocess function for video_filter_dashboard().
|
||||
*/
|
||||
function template_preprocess_video_filter_dashboard(&$variables) {
|
||||
// Construct page title.
|
||||
$variables['head_title'] = t('Video filter dashboard');
|
||||
$variables['head'] = drupal_get_html_head();
|
||||
$variables['help'] = theme('help');
|
||||
$variables['language'] = $GLOBALS['language'];
|
||||
$variables['language']->dir = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
|
||||
$variables['messages'] = isset($variables['show_messages']) ? theme('status_messages') : '';
|
||||
$variables['css'] = drupal_add_css();
|
||||
$variables['styles'] = drupal_get_css();
|
||||
$variables['scripts'] = drupal_get_js();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the dashboard.
|
||||
*/
|
||||
function video_filter_dashboard_page($editor) {
|
||||
module_invoke('admin_menu', 'suppress');
|
||||
// Add CSS.
|
||||
drupal_add_css(drupal_get_path('module', 'video_filter') . '/css/video_filter.css');
|
||||
|
||||
switch ($editor) {
|
||||
case 'wysiwyg_tinymce':
|
||||
// Add JavaScript.
|
||||
drupal_add_js(wysiwyg_get_path('tinymce') . '/jscripts/tiny_mce/tiny_mce_popup.js');
|
||||
drupal_add_js(drupal_get_path('module', 'video_filter') . '/editors/tinymce/video_filter.js');
|
||||
break;
|
||||
|
||||
case 'ckeditor':
|
||||
case 'wysiwyg_ckeditor':
|
||||
// Add JavaScript.
|
||||
drupal_add_js(drupal_get_path('module', 'video_filter') . '/editors/ckeditor/video_filter_dialog.js');
|
||||
break;
|
||||
|
||||
case 'fckeditor':
|
||||
case 'wysiwyg_fckeditor':
|
||||
// Add JavaScript.
|
||||
drupal_add_js(drupal_get_path('module', 'video_filter') . '/editors/fckeditor/video_filter/video_filter_dialog.js');
|
||||
break;
|
||||
}
|
||||
|
||||
print theme('video_filter_dashboard', array('form' => render(drupal_get_form('_video_filter_form'))));
|
||||
exit();
|
||||
}
|
||||
|
||||
function _video_filter_form() {
|
||||
$form['video_filter'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Insert Video'),
|
||||
'#weight' => 0,
|
||||
'#collapsible' => FALSE,
|
||||
'#collapsed' => FALSE,
|
||||
'#attributes' => array('class' => array('clearfix')),
|
||||
);
|
||||
|
||||
$form['video_filter']['file_url'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Video URL'),
|
||||
'#maxlength' => 255,
|
||||
'#size' => 80,
|
||||
'#default_value' => '',
|
||||
'#weight' => 1,
|
||||
);
|
||||
|
||||
$form['video_filter']['width'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Width'),
|
||||
'#maxlength' => 255,
|
||||
'#size' => 80,
|
||||
'#default_value' => '',
|
||||
'#weight' => 2,
|
||||
);
|
||||
|
||||
$form['video_filter']['height'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Height'),
|
||||
'#maxlength' => 255,
|
||||
'#size' => 80,
|
||||
'#default_value' => '',
|
||||
'#weight' => 3,
|
||||
);
|
||||
|
||||
$form['video_filter']['align'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Align'),
|
||||
'#default_value' => 'none',
|
||||
'#options' => array(
|
||||
'none' => t('None'),
|
||||
'left' => t('Left'),
|
||||
'right' => t('Right'),
|
||||
'center' => t('Center'),
|
||||
),
|
||||
'#weight' => 4,
|
||||
);
|
||||
|
||||
$form['video_filter']['autoplay'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Autoplay'),
|
||||
'#weight' => 5,
|
||||
);
|
||||
|
||||
$form['instructions'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Instructions'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#attributes' => array('class' => array('clearfix')),
|
||||
'#weight' => 97,
|
||||
);
|
||||
|
||||
$text = '<p>' . t('Insert a 3rd party video from one of the following providers.') . '</p>';
|
||||
$text .= _video_filter_instructions();
|
||||
|
||||
$form['instructions']['text'] = array(
|
||||
'#type' => 'item',
|
||||
'#markup' => $text,
|
||||
);
|
||||
|
||||
$form['cancel'] = array(
|
||||
'#type' => 'button',
|
||||
'#value' => t('Cancel'),
|
||||
'#weight' => 98,
|
||||
);
|
||||
|
||||
$form['insert'] = array(
|
||||
'#type' => 'button',
|
||||
'#value' => t('Insert'),
|
||||
'#weight' => 99,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
function video_filter_wysiwyg_plugin($editor, $version) {
|
||||
_video_filter_add_settings('wysiwyg_' . $editor);
|
||||
|
||||
$plugins = array();
|
||||
|
||||
switch ($editor) {
|
||||
case 'ckeditor':
|
||||
$plugins['video_filter'] = array(
|
||||
'path' => drupal_get_path('module', 'video_filter') . '/editors/ckeditor/',
|
||||
'buttons' => array('video_filter' => t('Video filter')),
|
||||
'url' => 'http://drupal.org/project/video_filter',
|
||||
'load' => TRUE,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'fckeditor':
|
||||
$plugins['video_filter'] = array(
|
||||
'path' => drupal_get_path('module', 'video_filter') . '/editors/fckeditor/',
|
||||
'buttons' => array('video_filter' => t('Video filter')),
|
||||
'url' => 'http://drupal.org/project/video_filter',
|
||||
'load' => TRUE,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'tinymce':
|
||||
$plugins['video_filter'] = array(
|
||||
'path' => drupal_get_path('module', 'video_filter') . '/editors/tinymce',
|
||||
'filename' => 'editor_plugin.js',
|
||||
'buttons' => array('video_filter' => t('Video filter')),
|
||||
'url' => 'http://drupal.org/project/video_filter',
|
||||
'load' => TRUE,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_element_info_alter().
|
||||
*/
|
||||
function video_filter_element_info_alter(&$types) {
|
||||
if (isset($types['text_format']['#pre_render']) && is_array($types['text_format']['#pre_render'])) {
|
||||
if (in_array('ckeditor_pre_render_text_format', $types['text_format']['#pre_render'])) {
|
||||
_video_filter_add_settings('ckeditor');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _video_filter_add_settings($editor) {
|
||||
static $editor_settings_added = array();
|
||||
static $global_settings_added = FALSE;
|
||||
|
||||
if (!isset($editor_settings_added[$editor])) {
|
||||
$editor_settings_added[$editor] = TRUE;
|
||||
|
||||
// Add popup url.
|
||||
$settings = array(
|
||||
'video_filter' => array('url' => array($editor => url('video_filter/dashboard/' . $editor))),
|
||||
);
|
||||
drupal_add_js($settings, 'setting');
|
||||
}
|
||||
|
||||
if (!$global_settings_added) {
|
||||
$global_settings_added = TRUE;
|
||||
|
||||
// Add global settings for video_filter.
|
||||
$settings = array(
|
||||
'video_filter' => array(
|
||||
'modulepath' => drupal_get_path('module', 'video_filter'),
|
||||
),
|
||||
);
|
||||
drupal_add_js($settings, 'setting');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses Codec into instructions for WYSIWYG popup.
|
||||
*/
|
||||
function _video_filter_instructions() {
|
||||
$codecs = video_filter_get_codec_info();
|
||||
$output = '<ul>';
|
||||
foreach ($codecs as $codec) {
|
||||
$output .= '<li><strong>' . $codec['name'] . '</strong><br />' . $codec['sample_url'] . '</li>';
|
||||
}
|
||||
$output .= '</ul>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests data from an oEmbed provider.
|
||||
*
|
||||
* Note: This function currently only supports JSON responses.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* The provider endpoint URL.
|
||||
* @param array $arguments
|
||||
* An associative array of URL arguments to send the provider.
|
||||
*
|
||||
* @return array|FALSE
|
||||
* An array of data if the request is successful, otherwise FALSE.
|
||||
*
|
||||
* @todo Support other response formats than JSON?
|
||||
*/
|
||||
function video_filter_oembed_request($endpoint, array $arguments) {
|
||||
// Make HTTP request.
|
||||
$result = drupal_http_request(url($endpoint, array('query' => $arguments)));
|
||||
|
||||
if ($data = json_decode($result->data)) {
|
||||
// Success.
|
||||
return (array) $data;
|
||||
}
|
||||
else {
|
||||
// Failure. Either the resource doesn't exist or there was an error with the
|
||||
// request.
|
||||
return FALSE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Video filter dashboard template, controls the output in the popup
|
||||
*/
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>">
|
||||
<head>
|
||||
<title><?php print $head_title; ?></title>
|
||||
<?php print $head; ?>
|
||||
<?php print $styles; ?>
|
||||
<?php print $scripts; ?>
|
||||
<script type="text/javascript"><?php /* Needed to avoid Flash of Unstyled Content in IE */ ?> </script>
|
||||
</head>
|
||||
|
||||
<body id="video_filter">
|
||||
<?php if (!empty($messages)): print $messages; endif; ?>
|
||||
<?php if (!empty($help)): print $help; endif; ?>
|
||||
<div class="clearfix">
|
||||
<?php print $form; ?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
@@ -0,0 +1,26 @@
|
||||
// $Id$
|
||||
|
||||
Add-on Module for Wysiwyg which provides button reordering and the insertion of
|
||||
separators.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Copy wysiwyg_buttonorder.module to your module directory and then enable on the
|
||||
admin modules page.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Button Order can be changed on the Wysiwyg profile configuration pages.
|
||||
Currently supports TinyMCE, CKEditor and FCKEditor.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
W. Vanheste
|
||||
rv0soft@gmail.com
|
||||
|
||||
partially developed @ Coworks Drupal Team
|
||||
http://www.coworks.net
|
@@ -0,0 +1,14 @@
|
||||
; $Id$
|
||||
name = Wysiwyg Button Order
|
||||
description = Allows reordering and seperators for TinyMCE in Wysiwyg module.
|
||||
package = User interface
|
||||
dependencies[] = wysiwyg
|
||||
core = 7.x
|
||||
|
||||
files[] = wysiwyg_button_order
|
||||
; Information added by drupal.org packaging script on 2013-01-29
|
||||
version = "7.x-1.0-rc1"
|
||||
core = "7.x"
|
||||
project = "wysiwyg_button_order"
|
||||
datestamp = "1359450976"
|
||||
|
@@ -0,0 +1,114 @@
|
||||
// $Id$
|
||||
/**
|
||||
* @file
|
||||
* Syncronizes the buttons checkboxes with the button order field.
|
||||
* Adds separators, removes buttons.
|
||||
*/
|
||||
(function ($) {
|
||||
Drupal.behaviors.WysiwygButtonOrder = {
|
||||
attach: function(context) {
|
||||
// add a button to add separators
|
||||
$('#buttonorder').parent().once().prepend(
|
||||
'<input type="button" class="form-submit" id="add-separator" value="' + Drupal.t('Add separator') + '" />'
|
||||
);
|
||||
$('#add-separator').once().click(function(e) {
|
||||
e.preventDefault();
|
||||
addDraggableRow('separator','---------------',Drupal.t('Separator'));
|
||||
return false;
|
||||
});
|
||||
|
||||
// add events to remove links
|
||||
$('.removelink').once().click(function(e) {
|
||||
e.preventDefault();
|
||||
var split = $(this).parent().parent().attr('id').split('-');
|
||||
var group = $(this).parent().prev().prev().text();
|
||||
var name = split[1];
|
||||
if (name != 'separator') unCheckButton(name,group);
|
||||
removeDraggableRow($(this).parent().parent().attr('id'));
|
||||
return false;
|
||||
});
|
||||
|
||||
// add events to sync button fieldset with order fieldset
|
||||
$('fieldset:nth-child(2) input.form-checkbox').once().click(function() {
|
||||
var split = $(this).attr('name').split('][');
|
||||
if (this.checked){
|
||||
addDraggableRow(split[1].slice(0,-1),split[0].substr(8),$(this).parent().text());
|
||||
} else {
|
||||
removeDraggableRow('order-' + split[1].slice(0,-1));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates draggable row for button
|
||||
* @args shortname: the short name of the button
|
||||
* @args group: the group name of the button belongs in
|
||||
*/
|
||||
function addDraggableRow(name,group,fullname) {
|
||||
if ($('#buttonorder').length > 0) {
|
||||
// make a full row
|
||||
var row = createDraggableRow(name,group,fullname);
|
||||
// add row to DOM tree
|
||||
$('#buttonorder tbody').append(row);
|
||||
// make it draggable
|
||||
Drupal.tableDrag['buttonorder'].makeDraggable(row.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes draggable row for button (shortname)
|
||||
*/
|
||||
function removeDraggableRow(id) {
|
||||
$('#'+id).remove();
|
||||
}
|
||||
|
||||
// amount of separators
|
||||
var seps = 0;
|
||||
$('.separator').each( function() {
|
||||
seps++;
|
||||
});
|
||||
/**
|
||||
* Creates a draggable row ready for insertion
|
||||
* @return DOM element = filled row with attached events
|
||||
*/
|
||||
function createDraggableRow(name,group,fullname){
|
||||
// if the name is a separator, make it unique
|
||||
var sepname = (name=='separator')? (name + '-' + ++seps) : '';
|
||||
// create the row(
|
||||
var row = $('<tr/>').addClass('draggable ' + ((sepname == '')? '' : 'separator ') + 'odd').attr('id','order-'+((sepname == '')? name : sepname));
|
||||
// append collumns
|
||||
$(row).append('<td>'+fullname+'</td>');
|
||||
$(row).append('<td><em>'+group+'</em></td>');
|
||||
$(row).append('<td class="tabledrag-hide" style="display: none;"></td>');
|
||||
$(row).append('<td><a href="#">Remove</a></td>');
|
||||
// Add "changed" to first collumn.
|
||||
$(row).find('td:first-child').append('<span class="warning tabledrag-changed">*</span>');
|
||||
// Add hidden weight component.
|
||||
$(row).find('td:nth-child(3)').append('<input type="hidden" name="buttonorder['+((sepname == '')? name : sepname)+'][weight]" id="edit-buttonorder-'+name+'-weight" value="1" class="buttons-weight">');
|
||||
|
||||
// add remove link to last element, include events
|
||||
$(row).find('td:last a').addClass('removelink').click(function(e) {
|
||||
e.preventDefault();
|
||||
// check again if it's a separator
|
||||
if(sepname == '') {
|
||||
unCheckButton(name,group);
|
||||
removeDraggableRow('order-'+name);//tabledrag-hide
|
||||
}
|
||||
else {
|
||||
removeDraggableRow('order-'+sepname);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// return the row
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncheck button in buttons fieldset
|
||||
*/
|
||||
function unCheckButton(name,group){
|
||||
$('#edit-buttons-'+group+'-'+name).attr('checked',false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery));
|
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main Wysiwyg Button Order system
|
||||
*
|
||||
* The Wysiwyg Button Order system integrates with the Wysiwyg module admin page
|
||||
* to provide the functionality of re-ordering buttons and adding separators
|
||||
* between them.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORMID_alter().
|
||||
*/
|
||||
function wysiwyg_button_order_form_wysiwyg_profile_form_alter(&$form, &$form_state) {
|
||||
|
||||
// Define currently supported editors.
|
||||
$supported = array(
|
||||
'tinymce',
|
||||
'fckeditor',
|
||||
'ckeditor',
|
||||
);
|
||||
|
||||
// Only do something when the editor is supported.
|
||||
if (in_array($form['editor']['#value'], $supported)) {
|
||||
// Add javascript.
|
||||
drupal_add_js(drupal_get_path('module', 'wysiwyg_button_order') . '/wysiwyg_button_order.js');
|
||||
|
||||
// Get the plugins, the format and the profile.
|
||||
$plugins = wysiwyg_get_plugins($form['editor']['#value']);
|
||||
$format = $form['format']['#value'];
|
||||
$profile = wysiwyg_get_profile($format);
|
||||
|
||||
// Build the form.
|
||||
$form['buttonorder'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Order and separators'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#tree' => TRUE,
|
||||
'#theme' => 'buttonorder_form',
|
||||
'#weight' => -1,
|
||||
);
|
||||
// Change the weight of existing items so that our form fits in a logical place.
|
||||
$form['buttons']['#weight'] = -2;
|
||||
$form['basic']['#weight'] = -3;
|
||||
|
||||
// Get buttonorder settings if they have been saved already.
|
||||
if (isset($profile->settings['buttonorder']) && !empty($profile->settings['buttonorder'])) {
|
||||
|
||||
$o_buttons = $profile->settings['buttonorder'];
|
||||
$o_buttons = explode(',', $o_buttons);
|
||||
// add increment to separators
|
||||
$count_sep = 0;
|
||||
foreach ($o_buttons as $k => $b) {
|
||||
$o_buttons[$k] = ($b == 'separator') ? $b . '-' . ++$count_sep : $b;
|
||||
}
|
||||
$o_buttons = array_flip($o_buttons);
|
||||
}
|
||||
|
||||
// Get active buttons.
|
||||
foreach ($plugins as $name => $meta) {
|
||||
if (isset($meta['buttons']) && is_array($meta['buttons'])) {
|
||||
foreach ($meta['buttons'] as $button => $title) {
|
||||
if (!empty($profile->settings['buttons'][$name][$button])) {
|
||||
$u_buttons[$button] = array(
|
||||
'group' => $name,
|
||||
'name' => $title
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (isset($meta['extensions']) && is_array($meta['extensions'])) {
|
||||
foreach ($meta['extensions'] as $extension => $title) {
|
||||
if (!empty($profile->settings['buttons'][$name][$extension])) {
|
||||
$u_buttons[$extension] = array(
|
||||
'group' => $name,
|
||||
'name' => $title
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($u_buttons)) {
|
||||
if (isset($o_buttons)) {
|
||||
foreach ($o_buttons as $key => $val) {
|
||||
if (isset($u_buttons[$key])) {
|
||||
$buttonset[$key] = $u_buttons[$key];
|
||||
}
|
||||
elseif ((substr($key, 0, 9) == 'separator')) {
|
||||
$buttonset[$key] = array(
|
||||
'group' => '---------------',
|
||||
'name' => t('Separator'),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$buttonset = $u_buttons;
|
||||
}
|
||||
|
||||
$order = 0;
|
||||
foreach ($buttonset as $name => $val) {
|
||||
$form['buttonorder'][$name]['name'] = array(
|
||||
'#markup' => $val['name'],
|
||||
);
|
||||
$form['buttonorder'][$name]['group'] = array(
|
||||
'#markup' => $val['group'],
|
||||
'#prefix' => '<em>',
|
||||
'#suffix' => '</em>',
|
||||
);
|
||||
$form['buttonorder'][$name]['weight'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#title' => t('Weight for @title', array('@title' => $val['name'])),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $order++,
|
||||
);
|
||||
$form['buttonorder'][$name]['remove'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => t('Remove'),
|
||||
'#href' => '#',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// add a submit function before the usual submit
|
||||
array_unshift($form['#submit'], 'wysiwyg_button_order_submit');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for wysiwyw_buttonorder_form().
|
||||
*/
|
||||
function wysiwyg_button_order_submit($form, &$form_state) {
|
||||
// drupal_set_message('<pre>' . print_r($form_state['input'], TRUE) . '</pre>');
|
||||
|
||||
if (!empty($form_state['input'])) {
|
||||
if (isset($form_state['input']['buttonorder'])) {
|
||||
$input_format = $form_state['values']['format'];
|
||||
|
||||
$buttonorder = FALSE;
|
||||
|
||||
foreach ($form_state['input']['buttonorder'] as $key => $val) {
|
||||
if (!is_null($val['weight'])) {
|
||||
if (substr($key, 0, 9) == 'separator') {
|
||||
$buttonorder[] = substr($key, 0, 9);
|
||||
}
|
||||
else {
|
||||
$buttonorder[] = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
// make our ordered buttonarray into a comma separated string
|
||||
$buttonorder = implode(',', $buttonorder);
|
||||
unset($form_state['values']['buttonorder']);
|
||||
$form_state['values']['buttonorder'] = $buttonorder;
|
||||
drupal_set_message(t('Buttons have be re-ordered for %format.', array('%format' => $input_format)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function wysiwyg_button_order_theme() {
|
||||
return array(
|
||||
'buttonorder_form' => array(
|
||||
'render element' => 'theme',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for the draggable wysiwyg buttonorder table.
|
||||
*/
|
||||
function theme_buttonorder_form($variables) {
|
||||
$form = $variables['theme'];
|
||||
$rows = array();
|
||||
|
||||
if (element_children($form)) {
|
||||
foreach (element_children($form) as $name) {
|
||||
$form[$name]['weight']['#attributes']['class'] = array('buttons-weight');
|
||||
$form[$name]['group']['#attributes']['class'] = array('buttons-group');
|
||||
$form[$name]['name']['#attributes']['class'] = array('buttons-name');
|
||||
$form[$name]['remove']['#attributes']['class'] = array('removelink');
|
||||
|
||||
$rows[] = array(
|
||||
'data' => array(
|
||||
drupal_render($form[$name]['name']),
|
||||
drupal_render($form[$name]['group']),
|
||||
drupal_render($form[$name]['weight']),
|
||||
drupal_render($form[$name]['remove']),
|
||||
),
|
||||
'class' => (substr($name, 0, 9) == 'separator' ? array('draggable', 'separator') : array('draggable')),
|
||||
'id' => 'order-' . $name,
|
||||
);
|
||||
}
|
||||
$header = array(t('Name'), t('Group'), t('Weight'), array('data' => t('Operations')));
|
||||
$output = theme('table', array(
|
||||
'header' => $header,
|
||||
'rows' => $rows,
|
||||
'attributes' => array('id' => 'buttonorder'),
|
||||
));
|
||||
$output .= drupal_render_children($form);
|
||||
|
||||
drupal_add_tabledrag('buttonorder', 'order', 'sibling', 'buttons-weight');
|
||||
|
||||
return $output;
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('To use the Wysiwyg Button Order module, enable some buttons, save, then return to this page.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_settings_alter().
|
||||
*/
|
||||
function wysiwyg_button_order_wysiwyg_editor_settings_alter(&$settings, $context) {
|
||||
// TinyMCE.
|
||||
if ($context['profile']->editor == 'tinymce') {
|
||||
$which = $context['profile']->format;
|
||||
$row = db_query("SELECT w.format, ff.name, w.settings FROM {wysiwyg} w INNER JOIN {filter_format} ff ON ff.format=w.format WHERE w.format = :which", array(':which' => $which))->fetch();
|
||||
if (isset($row->settings)) {
|
||||
$things = unserialize($row->settings);
|
||||
// check if a button order has been set, if true, load the buttonorder
|
||||
if (isset($things['buttonorder'])) {
|
||||
$settings['theme_advanced_buttons1'] = $things['buttonorder'];
|
||||
}
|
||||
}
|
||||
}
|
||||
// FCKeditor and CKeditor.
|
||||
if ($context['profile']->editor == 'fckeditor' || $context['profile']->editor == 'ckeditor') {
|
||||
$which = $context['profile']->format;
|
||||
$row = db_query("SELECT w.format, ff.name, w.settings FROM {wysiwyg} w INNER JOIN {filter_format} ff ON ff.format=w.format WHERE w.format = :which", array(':which' => $which))->fetch();
|
||||
if (isset($row->settings)) {
|
||||
$things = unserialize($row->settings);
|
||||
// check if a button order has been set, if true, load the buttonorder
|
||||
if (isset($things['buttonorder'])) {
|
||||
$things = explode(',', $things['buttonorder']);
|
||||
array_walk($things, create_function('&$val', 'if($val=="separator") $val = "-";'));
|
||||
switch ($context['profile']->editor) {
|
||||
case 'fckeditor' : $settings['buttons'][0] = $things;
|
||||
break;
|
||||
case 'ckeditor' : $settings['toolbar'][0] = $things;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
|
||||
6.x-1.x-dev
|
||||
===========
|
||||
|
||||
Features:
|
||||
- #878032 by dagmar - Integrate with input formats module.
|
||||
|
||||
Bugfixes:
|
||||
- #926364 by roderik - Filtering occurring when filter is turned off.
|
||||
|
||||
|
||||
6.x-1.5 - 2010/05/08
|
||||
====================
|
||||
|
||||
Features:
|
||||
- #713922 Provide the valid_elements option to TinyMCE editors dynamically altering the Wysiwyg API settings.
|
||||
- #775200 Enhance the handling of HTML comments (solution based on #559584).
|
||||
|
||||
Bugfixes:
|
||||
- #746434 by nedjo - Filter form validate and submit handlers overwritten.
|
||||
- #760720 by gaellafond and markus_petrux - margin/padding style attribute gets stripped when using more than one value.
|
||||
- #792566 Issues with border-width, border-style and border-color (marked as dup, fixed in #760720).
|
||||
- #788508 Enhance the parser to accept individual values of background style attribute in any order.
|
||||
- #789690 Enhance the parser to accept individual values of font style attribute in any order.
|
||||
|
||||
|
||||
6.x-1.4 - 2009/12/27
|
||||
====================
|
||||
|
||||
Features:
|
||||
- #574686 Aside from fixing the bug, code splitted into external includes to minimize memory requirements when the filter is not used.
|
||||
- Added CHANGELOG.txt.
|
||||
- Updated translation template to account for changes in code split.
|
||||
|
||||
Bugfixes:
|
||||
- Fixed minor coding style issues reported by Coder module.
|
||||
- #574686 WYSIWYG Filter Strips out allowed HTML when filter settings have not been saved.
|
||||
- Fixed named entities handling, so it does not clash with other entities and result in wrong encoding. (back port of Drupal issue #359276).
|
||||
|
||||
|
||||
6.x-1.3 - 2009/07/08
|
||||
====================
|
||||
|
||||
Bugfixes:
|
||||
- #484916 by sinasquax - Bug with attributes with default values when they are not declared in the content.
|
||||
|
||||
|
||||
6.x-1.2 - 2009/02/24
|
||||
====================
|
||||
|
||||
Features:
|
||||
- #368682 Accept style property names in uppercase.
|
||||
- Integration with checkall add-on module for style property checkboxes.
|
||||
|
||||
Bugfixes:
|
||||
- #363284 Filters out URLs no matter what you put into the filter settings.
|
||||
|
||||
|
||||
6.x-1.1 - 2008/11/23
|
||||
====================
|
||||
|
||||
- #338181 by kroimon - Inline-style color in rbg() format not working.
|
||||
|
||||
|
||||
6.x-1.0 - 2008/11/02
|
||||
====================
|
||||
|
||||
- Changed tips output (do not generate paragraphs).
|
||||
- Changed default nofollow policy to whitelist (own site domain).
|
||||
- Added span to default valid elements.
|
||||
- Fixed regular expression for percents.
|
||||
- Added explicit regular expression for bgcolors (this one just means a bit less code, hence it's easier to maintain too).
|
||||
|
||||
|
||||
6.x-1.0-rc1 - 2008/10/26
|
||||
========================
|
||||
|
||||
- First release candidate.
|
339
sites/all/modules/contrib/editor/wysiwyg_filter/LICENSE.txt
Normal file
339
sites/all/modules/contrib/editor/wysiwyg_filter/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
84
sites/all/modules/contrib/editor/wysiwyg_filter/README.txt
Normal file
84
sites/all/modules/contrib/editor/wysiwyg_filter/README.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; WYSIWYG Filter module for Drupal
|
||||
;;
|
||||
;; Original author: markus_petrux at drupal.org (October 2008)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
OVERVIEW
|
||||
========
|
||||
|
||||
The WYSIWYG Filter module provides an input filter that allows site
|
||||
administrators configure which HTML elements, attributes and style properties
|
||||
are allowed. It also may add rel="nofollow" to posted links based on filter
|
||||
options. It can do so with no additional parsing on user input. That is, it may
|
||||
apply nofollow rules while parsing HTML elements and attributes.
|
||||
|
||||
The filter is based on whitelists that can be defined from the filter settings
|
||||
panel. Rules for HTML element and attributes are defined using the same syntax
|
||||
of the TinyMCE valid_elements option.
|
||||
|
||||
The following elements cannot be whitelisted due to security reasons, to
|
||||
prevent users from breaking site layout and/or to avoid posting invalid HTML.
|
||||
Forbidden elements: applet, area, base, basefont, body, button, embed, form,
|
||||
frame, frameset, head, html, iframe, input, isindex, label, link, map, meta,
|
||||
noframes, noscript, object, optgroup, option, param, script, select, style,
|
||||
textarea, title.
|
||||
|
||||
The section used to whitelist style properties is pretty simple. You just check
|
||||
the properties you need from a list where almost all style properties are
|
||||
organized into logical groups (Color and Background properties, Font, Text,
|
||||
Box, Table, List, ...). The WYSIWYG Filter will strip out style properties not
|
||||
explicitly enabled. On the other hand, for allowed style properties the WYSIWYG
|
||||
Filter will check their values for strict CSS syntax (based on regular
|
||||
expressions) and strip out those that do not match. Additional matching rules
|
||||
are explicitly required for properties that may contain URLs in their values
|
||||
("background", "background-image", "list-style" and "list-style-image"). If
|
||||
rules don't match, these style properties will be ignored from user input.
|
||||
|
||||
When the "id" and "class" attributes have been whitelisted, it is also required
|
||||
to specify explicit rules that will be used to validate user input, and again,
|
||||
those that don't match will be stripped out.
|
||||
|
||||
As a measure to reduce the effectiveness of spam links, it is often recommended
|
||||
to add rel="nofollow" to posted links leading to external sites. The WYSIWYG
|
||||
Filter can easily do this for you while HTML is being processed with almost no
|
||||
additional performance impact. There is a section in the filter settings panel
|
||||
where a white/back list policy can be defined per domain name (the host part in
|
||||
the URLs).
|
||||
|
||||
|
||||
INSTALLATION
|
||||
============
|
||||
|
||||
- Copy all contents of this package to your modules directory preserving
|
||||
subdirectory structure.
|
||||
|
||||
- Goto admin/build/modules to install the module.
|
||||
|
||||
- Goto admin/settings/filters and create a new input format as follows:
|
||||
|
||||
- Input format name: WYSIWYG Filter (or something similar of your choice).
|
||||
- Check the filters: WYSIWYG Filter and HTML Corrector. Save.
|
||||
- Goto Rearrange tab.
|
||||
- Drag the WYSIWYG Filter on top of the HTML Corrector. Save.
|
||||
- Goto the Configure tab of your newly created WYSIWYG Filter and setup the
|
||||
available options to suit your needs.
|
||||
|
||||
|
||||
SECURITY ISSUES
|
||||
===============
|
||||
|
||||
- To report security issues, do not use the issue tracker of the module.
|
||||
Instead, please contact the Drupal Security Team or the WYSIWYG Filter
|
||||
module developer (preferred).
|
||||
|
||||
- To contact the WYSIWYG Filter module developer:
|
||||
http://drupal.org/user/39593
|
||||
http://drupal.org/user/39593/contact
|
||||
|
||||
- To contact the Drupal Security Team:
|
||||
http://drupal.org/security-team
|
||||
|
||||
- For any other kind of issue (support or feature requests, bug reports,
|
||||
translations, etc.), please, use the issue tracker of the module:
|
||||
http://drupal.org/project/issues/wysiwyg_filter
|
@@ -0,0 +1,6 @@
|
||||
|
||||
.wysiwyg-filter-style-properties-group {
|
||||
float: left;
|
||||
margin-right: 2em;
|
||||
width: 25%;
|
||||
}
|
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Administration pages for the WYSIWYG Filter module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_filter_FILTER_settings
|
||||
*
|
||||
* @ingroup forms
|
||||
* TODO:
|
||||
* * remove defaults from info hook
|
||||
* * set defaults in helper func
|
||||
* * save settings for other formats in hidden form items
|
||||
* * remove format suffix, will be first array index
|
||||
* * care for pre- and post-processing, or remove it like parsed-elements
|
||||
* * rewrite validate to get correct values like $form['filters']['settings'][$name] / $form_state['values']['filters'][$name]['settings']
|
||||
*
|
||||
*/
|
||||
function wysiwyg_filter_filter_wysiwyg_settings(&$form, &$form_state, $filter, $format, $defaults, $filters) {
|
||||
global $base_url;
|
||||
drupal_add_css(drupal_get_path('module', 'wysiwyg_filter') . '/wysiwyg_filter.admin.css', array('preprocess' => FALSE));
|
||||
// Load common functions.
|
||||
module_load_include('inc', 'wysiwyg_filter');
|
||||
|
||||
$settings = $filter->settings;
|
||||
$settings += $defaults;
|
||||
|
||||
// carry over settings for other formats
|
||||
$filterform = array();
|
||||
|
||||
// *** valid elements ***
|
||||
$valid_elements = $settings['valid_elements'];
|
||||
$valid_elements_rows = min(20, max(5, substr_count($valid_elements, "\n") + 2));
|
||||
// show blacklisted elements in description
|
||||
$elements_blacklist = wysiwyg_filter_get_elements_blacklist();
|
||||
foreach ($elements_blacklist as $i => $element) {
|
||||
$elements_blacklist[$i] = '<' . $element . '>';
|
||||
}
|
||||
$filterform['valid_elements'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('HTML elements and attributes'),
|
||||
'#default_value' => $valid_elements,
|
||||
'#cols' => 60,
|
||||
'#rows' => $valid_elements_rows,
|
||||
'#description' => t('<p>
|
||||
This option allows you to specify which HTML elements and attributes are allowed in <a href="@valid-elements">TinyMCE valid_elements format</a>.
|
||||
</p>
|
||||
<strong>Syntax tips:</strong><ul>
|
||||
<li>Use a comma separated list to allow several HTML elements. Example: "em,strong,br,p,ul,ol,li". Note that you can split your definitions using any number of lines.</li>
|
||||
<li>Use square brackets "[]" to specify the attributes that are allowed for each HTML element. Attributes should be whitelisted explicitly, otherwise element attributes will be ignored. Example: "a" will NOT allow users to post links, you should use "a[href]" instead!</li>
|
||||
<li>Use the vertical bar character "|" to separate several attribute definitions for a single HTML element. Example: "a[href|target]" means users may optionally specify the "href" and "target" attributes for "a" elements, any other attribute will be ignored.</li>
|
||||
<li>Use the exclamation mark "!" to set one attribute as being required for a particular HTML element. Example: "a[!href|target]" means users must specify the "href" attribute, otherwise the whole "a" element will be ignored. Users may optionally specify the "target" attribute as well. However, any other attribute will be ignored.</li>
|
||||
<li>Use the asterisk symbol "*" to whitelist all possible attributes for a particular HTML element. Example: "a[*]" means users will be allowed to use any attribute for the "a" element.</li>
|
||||
<li>Use the at sign character "@" to whitelist a common set of attributes for all allowed HTML elements. Example: "@[class|style]" means users will be allowed to use the "class" and "style" attributes for any whitelisted HTML element.</li>
|
||||
<li>For further information and examples, please consult documentation of the <a href="@valid-elements">valid_elements</a> option in the TinyMCE Wiki site.</li>
|
||||
</ul>
|
||||
<strong>Additional notes:</strong><ul>
|
||||
<li>The following elements cannot be whitelisted due to security reasons, to prevent users from breaking site layout and/or to avoid posting invalid HTML. Forbidden elements: %elements-blacklist.</li>
|
||||
<li>JavaScript event attributes such as onclick, onmouseover, etc. are always ignored. Should you need them, please consider using the "Full HTML" input format instead.</li>
|
||||
<li>If you allow usage of the attributes "id", "class" and/or "style", then you should also select which style properties are allowed and/or specify explicit matching rules for them using the "Advanced rules" section below.</li>
|
||||
</ul>', array(
|
||||
'@valid-elements' => 'http://wiki.moxiecode.com/index.php/TinyMCE:Configuration/valid_elements',
|
||||
'%elements-blacklist' => implode(' ', $elements_blacklist),
|
||||
)),
|
||||
);
|
||||
|
||||
// *** allow comments ***
|
||||
$filterform['allow_comments'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('HTML comments'),
|
||||
'#options' => array(
|
||||
0 => t('Disabled'),
|
||||
1 => t('Enabled'),
|
||||
),
|
||||
'#default_value' => $settings['allow_comments'],
|
||||
'#description' => t('Use this option to allow HTML comments.'),
|
||||
);
|
||||
|
||||
// *** Style properties ***
|
||||
$filterform['styles'] = array(
|
||||
'#title' => t('Style properties'),
|
||||
'#description' => '<p>' . t('This section allows you to select which style properties can be used for HTML elements where the "style" attribute has been allowed. The <em>WYSIWYG Filter</em> will strip out style properties (and their values) not explicitly enabled here. On the other hand, for allowed style properties the <em>WYSIWYG Filter</em> will check their values for strict CSS syntax and strip out those that do not match.') . '</p>' .
|
||||
'<p>' . t('Additional matching rules should be specified from the "Advanced rules" section below for a few of these properties that may contain URLs in their values ("background", "background-image", "list-style" and "list-style-image"). Otherwise, these style properties will be ignored from user input.') . '</p>',
|
||||
);
|
||||
$style_property_groups = wysiwyg_filter_get_style_property_groups();
|
||||
$enabled_style_properties = array();
|
||||
$i = 0;
|
||||
foreach ($style_property_groups as $group => $group_info) {
|
||||
$style_properties = $settings["style_$group"];
|
||||
$enabled_style_properties += array_filter($style_properties);
|
||||
$filterform["style_$group"] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $group_info['title'],
|
||||
'#default_value' => $style_properties,
|
||||
'#options' => drupal_map_assoc(array_keys($group_info['properties'])),
|
||||
'#checkall' => TRUE,
|
||||
'#prefix' => '<div class="wysiwyg-filter-style-properties-group">',
|
||||
'#suffix' => '</div>'. (($i % 3) == 2?'<br style="clear:both"/>':''),
|
||||
);
|
||||
$i++;
|
||||
}
|
||||
|
||||
// *** Advanced rules ***
|
||||
$filterform['rules'] = array(
|
||||
'#title' => t('Advanced rules'),
|
||||
'#prefix' => '<br style="clear:both"/>',
|
||||
'#description' => '<p>' . t('Use the following options to configure additional rules for certain HTML element attributes. As a safety measure, these rules should be defined explicitly. Otherwise, the corresponding HTML element attributes will be ignored from user input.') . '</p>',
|
||||
);
|
||||
$valid_elements_parsed = wysiwyg_filter_parse_valid_elements($settings['valid_elements']);
|
||||
foreach (wysiwyg_filter_get_advanced_rules() as $rule_key => $rule_info) {
|
||||
$field_name = "rule_$rule_key";
|
||||
$default_value = wysiwyg_filter_array2csv($settings[$field_name]);
|
||||
$filterform[$field_name] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => $rule_info['title'],
|
||||
'#default_value' => $default_value,
|
||||
'#cols' => 60,
|
||||
'#rows' => min(10, max(2, substr_count($default_value, "\n") + 2)),
|
||||
'#description' => $rule_info['description'],
|
||||
);
|
||||
|
||||
// Display warning if the field is empty but the rule definition is not
|
||||
// complete.
|
||||
if (empty($default_value) && !_wysiwyg_filter_is_rule_definition_complete($rule_info, $valid_elements_parsed, $enabled_style_properties)) {
|
||||
drupal_set_message($rule_info['required_by_message'], 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
// *** Nofollow properties ***
|
||||
$filterform['nofollow'] = array(
|
||||
'#title' => t('Spam link deterrent settings'),
|
||||
'#description' => t('As a measure to reduce the effectiveness of spam links, it is often recommended to add rel="nofollow" to posted links leading to external sites. The WYSIWYG Filter can easily do this for you while HTML is being processed with almost no additional performance impact.'),
|
||||
);
|
||||
$filterform['nofollow_policy'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Policy'),
|
||||
'#options' => array(
|
||||
'disabled' => t('Disabled - Do not add rel="nofollow" to any link.'),
|
||||
'whitelist' => t('Whitelist - Add rel="nofollow" to all links except those leading to domain names specified in the list below.'),
|
||||
'blacklist' => t('Blacklist - Add rel="nofollow" to all links leading to domain names specified in the list below.'),
|
||||
),
|
||||
'#default_value' => $settings['nofollow_policy'],
|
||||
'#description' => t('If you choose the whitelist option, be sure to add your own domain names to the list!'),
|
||||
);
|
||||
$parts = parse_url($base_url);
|
||||
// Note that domains list is stored by our submit handler in array form where
|
||||
// dots have been escaped, so we need here to revert the process to get a clean
|
||||
// string for user input where dots are unescaped.
|
||||
$nofollow_domains = wysiwyg_filter_array2csv($settings['nofollow_domains']);
|
||||
$filterform['nofollow_domains'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Domains list'),
|
||||
'#default_value' => $nofollow_domains,
|
||||
'#cols' => 60,
|
||||
'#rows' => min(10, max(5, substr_count($nofollow_domains, "\n") + 2)),
|
||||
'#description' => t('Enter a comma separated list of top level domain names. Note that all subdomains will also be included. Example: example.com will match example.com, www.example.com, etc.'),
|
||||
);
|
||||
|
||||
return $filterform;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements hook_form_FORM_ID_alter
|
||||
*
|
||||
* add validate and submit handlers
|
||||
*/
|
||||
function wysiwyg_filter_form_filter_admin_format_form_alter(&$form, &$form_state, $form_id) {
|
||||
$form['#validate'][] = 'wysiwyg_filter_filter_wysiwyg_settings_validate';
|
||||
// Add the submit callback to the beginning of the array because we need
|
||||
// to prepare data for system_settings_form_submit().
|
||||
array_unshift($form['#submit'], 'wysiwyg_filter_filter_wysiwyg_settings_submit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if the given rule definition is complete.
|
||||
*
|
||||
* @param $rule_info
|
||||
* An array of information about the rule we are about to check.
|
||||
* @param $elements
|
||||
* The array of all valid elements enabled for the current filter.
|
||||
* @param $style_properties
|
||||
* The array of all style properties enabled for the current filter.
|
||||
* @return
|
||||
* TRUE if the rule definiton is complete, FALSE otherwise.
|
||||
*
|
||||
* @see wysiwyg_filter_parse_valid_elements()
|
||||
* @see wysiwyg_filter_get_advanced_rules()
|
||||
*/
|
||||
function _wysiwyg_filter_is_rule_definition_complete($rule_info, $elements, $style_properties) {
|
||||
foreach ($elements as $tag => $attributes) {
|
||||
if (isset($attributes[$rule_info['required_by']])) {
|
||||
// If this rule is not dependent on style properties, then we found it,
|
||||
// while we should not, so the rule is not complete.
|
||||
if (empty($rule_info['required_by_styles'])) {
|
||||
return FALSE;
|
||||
}
|
||||
// If this rule is dependent on style properties, then we need to check
|
||||
// if the related style properties exist.
|
||||
foreach ($rule_info['required_by_styles'] as $style_property) {
|
||||
if (isset($style_properties[$style_property])) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any warning message we might have set previously.
|
||||
*/
|
||||
function _wysiwyg_filter_clear_messages() {
|
||||
$messages = drupal_get_messages('warning');
|
||||
if (!empty($messages)) {
|
||||
foreach (wysiwyg_filter_get_advanced_rules() as $rule_info) {
|
||||
$my_messages[] = $rule_info['required_by_message'];
|
||||
}
|
||||
foreach ($messages['warning'] as $warning) {
|
||||
if (!in_array($warning, $my_messages)) {
|
||||
drupal_set_message($warning, 'warning');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate filter settings form.
|
||||
*
|
||||
* @ingroup forms
|
||||
*/
|
||||
function wysiwyg_filter_filter_wysiwyg_settings_validate($form, &$form_state) {
|
||||
$values =& $form_state['values']['filters']['wysiwyg']['settings'];
|
||||
|
||||
// *** validate valid_elements ***
|
||||
// Check elements against hardcoded backlist.
|
||||
$elements_blacklist = wysiwyg_filter_get_elements_blacklist();
|
||||
$valid_elements = trim($values['valid_elements']);
|
||||
$valid_elements = wysiwyg_filter_parse_valid_elements($valid_elements);
|
||||
$forbidden_elements = array();
|
||||
foreach (array_keys($valid_elements) as $element) {
|
||||
if (in_array($element, $elements_blacklist)) {
|
||||
$forbidden_elements[] = $element;
|
||||
}
|
||||
}
|
||||
if (!empty($forbidden_elements)) {
|
||||
form_set_error('valid_elements', t('The following elements cannot be allowed: %elements.', array('%elements' => implode(', ', $forbidden_elements))));
|
||||
}
|
||||
|
||||
// *** validate nofollow_domains ***
|
||||
foreach (wysiwyg_filter_get_advanced_rules() as $rule_key => $rule_info) {
|
||||
$field_name = "rule_$rule_key";
|
||||
$expressions = array_filter(explode(',', preg_replace('#\s+#', ',', trim($values[$field_name])))); // form2db
|
||||
$errors = array();
|
||||
foreach ($expressions as $expression) {
|
||||
if (preg_match('`[*?]\*|\*\?`', $expression)) {
|
||||
$errors[] = t('Invalid expression %expression. Please, do not use more than one consecutive asterisk (**) or one that is next to a question mark wildcard (?* or *?).', array('%expression' => $expression));
|
||||
}
|
||||
if (!preg_match($rule_info['validate_regexp'], $expression)) {
|
||||
$errors[] = t('Invalid expression %expression. Please, check the syntax of the %field field.', array('%expression' => $expression, '%field' => $rule_info['title']));
|
||||
}
|
||||
}
|
||||
if (!empty($errors)) {
|
||||
form_set_error($field_name, implode('<br />', $errors));
|
||||
}
|
||||
}
|
||||
|
||||
// *** validate nofollow_domains ***
|
||||
$nofollow_domains = array_filter(explode(',', preg_replace('#\s+#', ',', $values['nofollow_domains']))); // form2db
|
||||
foreach ($nofollow_domains as $nofollow_domain) {
|
||||
if (!preg_match('#^([a-z0-9]([-a-z0-9]*)?\.)+([a-z]+)$#i', $nofollow_domain)) {
|
||||
form_set_error('nofollow_domains', t('Invalid domain %domain. Please, enter a comma separated list of valid domain names.', array('%domain' => $nofollow_domain)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit processing for the filter settings form.
|
||||
*
|
||||
* Parse filter options to help us save resources that would otherwiese
|
||||
* require time and precious cpu cycles at filter processing time.
|
||||
*
|
||||
* @ingroup forms
|
||||
*/
|
||||
function wysiwyg_filter_filter_wysiwyg_settings_submit($form, &$form_state) {
|
||||
$values =& $form_state['values']['filters']['wysiwyg']['settings'];
|
||||
|
||||
// *** prepare valid_elements - just trim ***
|
||||
$values['valid_elements'] = trim($values['valid_elements']);
|
||||
|
||||
// *** prepare rules - csv2array ***
|
||||
foreach (array_keys(wysiwyg_filter_get_advanced_rules()) as $rule_key) {
|
||||
$field_name = "rule_$rule_key";
|
||||
$values[$field_name] = wysiwyg_filter_csv2array($values[$field_name]);
|
||||
}
|
||||
|
||||
// *** prepare nofollow_domains - csv2array ***
|
||||
$values['nofollow_domains'] = wysiwyg_filter_csv2array($values['nofollow_domains']);
|
||||
}
|
||||
|
||||
/*
|
||||
* CSV to Array
|
||||
*
|
||||
* @param atring $v
|
||||
* @param bool $space2comma - shall we convet whitespace to commas before processing?
|
||||
* @return array
|
||||
*/
|
||||
function wysiwyg_filter_csv2array($v, $space2comma = TRUE) {
|
||||
IF($space2comma) $v = preg_replace('#\s+#', ',', $v);
|
||||
return array_filter(explode(',', $v));
|
||||
}
|
||||
/*
|
||||
* Array to CSV
|
||||
*
|
||||
* @param array $v
|
||||
* @return string
|
||||
*/
|
||||
function wysiwyg_filter_array2csv($v, $separator = ",\n") {
|
||||
return implode($separator, $v);
|
||||
}
|
@@ -0,0 +1,529 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Common functions for the WYSIWYG Filter module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper function to get information about fields that implement
|
||||
* advanced rules.
|
||||
*
|
||||
* @see wysiwyg_filter_get_filter_options()
|
||||
* @see wysiwyg_filter_settings_filter()
|
||||
* @see wysiwyg_filter_settings_filter_validate()
|
||||
* @see wysiwyg_filter_settings_filter_submit()
|
||||
*/
|
||||
function wysiwyg_filter_get_advanced_rules() {
|
||||
global $base_url, $base_path;
|
||||
return array(
|
||||
'valid_classes' => array(
|
||||
'title' => t('Rules for Class Names'),
|
||||
'description' => t('Enter a comma separated list of rules for <em>Class Names</em>. Whitespaces and line-breaks are ignored. <em>Class Names</em> should start with an upper or lower case letter "a to z" and can be followed by one or more upper or lower case letters "a to z", digits "0 to 9", hyphens "-" and/or underscores "_". The asterisk character "*" can be used in rules to represent any number of characters from the second position of the rule. Example: "userclass*, my-font-*" are valid rules for <em>Class Names</em>, whereas "*class" is invalid.'),
|
||||
'validate_regexp' => '`^[a-zA-Z][-_a-zA-Z0-9?*]*$`',
|
||||
'asterisk_expansion' => '[-_a-zA-Z0-9]*',
|
||||
'required_by' => 'class',
|
||||
'required_by_message' => t('The <strong>class</strong> attribute is used in your <em>HTML elements and attributes</em> rules. You should specify the <em>Rules for Class Names</em> field in the "Advanced rules" section below. Leaving it unspecified will result in all class attributes filtered out.'),
|
||||
),
|
||||
'valid_ids' => array(
|
||||
'title' => t('Rules for Element IDs'),
|
||||
'description' => t('Enter a comma separated list of rules for <em>Element IDs</em>. Whitespaces and line-breaks are ignored. <em>Element IDs</em> should start with an upper or lower case letter "a to z" and can be followed by one or more upper or lower case letters "a to z", digits "0 to 9", hyphens "-" and/or underscores "_". The asterisk character "*" can be used in rules to represent any number of characters from the second position of the rule. Example: "foo*" is a valid rule for <em>Element IDs</em>, whereas "*bar" is invalid.'),
|
||||
'validate_regexp' => '`^[a-zA-Z][-_a-zA-Z0-9?*]*$`',
|
||||
'asterisk_expansion' => '[-_a-zA-Z0-9]*',
|
||||
'required_by' => 'id',
|
||||
'required_by_message' => t('The <strong>id</strong> attribute is used in your <em>HTML elements and attributes</em> rules. You should specify the <em>Rules for Element IDs</em> field in the "Advanced rules" section below. Leaving it unspecified will result in all id attributes filtered out.'),
|
||||
),
|
||||
'style_urls' => array(
|
||||
'title' => t('Rules for URLs used within inline styles'),
|
||||
'description' => t('Enter a comma separated list of rules for <em>URLs used within inline styles</em>. Whitespaces and line-breaks are ignored. These rules affect the following style properties: "background", "background-image", "list-style" and "list-style-image". Each rule represents a single path or URL. The asterisk character "*" can be used to represent any number of characters. Examples: use "/*" for local URLs only, use "/images/*" for one particular directory, use "http://www.example.com/*" for URLs of an external site, use "@base-path*, @base-url*" for URLs of your own site.', array('@base-path' => $base_path, '@base-url' => $base_url)),
|
||||
'validate_regexp' => '`^.*$`',
|
||||
'asterisk_expansion' => '.*',
|
||||
'required_by' => 'style',
|
||||
'required_by_styles' => array('background', 'background-image', 'list-style', 'list-style-image'),
|
||||
'required_by_message' => t('The <strong>style</strong> attribute is used in your <em>HTML elements and attributes</em> rules, and you have enabled one of the following style properties: "background", "background-image", "list-style" or "list-style-image". You should specify the <em>Rules for URLs used within inline styles</em> field in the "Advanced rules" section below. Leaving it unspecified will result in all URLs used within inline styles filtered out.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a string with default valid_elements.
|
||||
*/
|
||||
function wysiwyg_filter_default_valid_elements() {
|
||||
return <<<EOT
|
||||
a[!href|target<_blank|title],
|
||||
div[align<center?justify?left?right],
|
||||
p[align<center?justify?left?right],
|
||||
br,span,em,strong,cite,code,blockquote,ul,ol,li,dl,dt,dd
|
||||
EOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML elements blacklist.
|
||||
*/
|
||||
function wysiwyg_filter_get_elements_blacklist() {
|
||||
return array(
|
||||
'applet',
|
||||
'area',
|
||||
'base',
|
||||
'basefont',
|
||||
'body',
|
||||
'button',
|
||||
'embed',
|
||||
'form',
|
||||
'frame',
|
||||
'frameset',
|
||||
'head',
|
||||
'html',
|
||||
'iframe',
|
||||
'input',
|
||||
'isindex',
|
||||
'label',
|
||||
'link',
|
||||
'map',
|
||||
'meta',
|
||||
'noframes',
|
||||
'noscript',
|
||||
'object',
|
||||
'optgroup',
|
||||
'option',
|
||||
'param',
|
||||
'script',
|
||||
'select',
|
||||
'style',
|
||||
'textarea',
|
||||
'title',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse valid_elements string in TinyMCE format.
|
||||
*
|
||||
* @link http://wiki.moxiecode.com/index.php/TinyMCE:Configuration/valid_elements
|
||||
*
|
||||
* @param string $valid_elements
|
||||
*
|
||||
* @return array
|
||||
* Information about allowed HTML elements and attributes.
|
||||
* Each HTML element contains a whitelist of attributes or '*' meaning all
|
||||
* attributes are allowed for that element.
|
||||
* Each attribute contains an array with one or more of the following items
|
||||
* of information:
|
||||
* - required boolean TRUE when attribute is required.
|
||||
* - default string Default value that will be applied when only the
|
||||
* attribute name is specified.
|
||||
* - forced string Value that will be applied when the attribute is
|
||||
* present in the parsed HTML stream.
|
||||
* - values array Whitelist of attribute values.
|
||||
* This information is used by the WYSIWYG Filter itself to filter out
|
||||
* disallowed HTML elements and attributes.
|
||||
*
|
||||
* @see wysiwyg_filter_process()
|
||||
*/
|
||||
function wysiwyg_filter_parse_valid_elements($valid_elements) {
|
||||
// Remove whitespaces and split valid elements from a comma separate list of items into an array.
|
||||
$valid_elements = array_map('drupal_strtolower', array_filter(explode(',', preg_replace('#\s+#', '', $valid_elements))));
|
||||
$parsed_elements = array();
|
||||
$common_attributes = array();
|
||||
foreach ($valid_elements as $valid_element) {
|
||||
// Extract the element name and its allowed attributes list
|
||||
// including special characters that will be parsed later.
|
||||
if (preg_match('`^(@|[#+-]{0,1}[a-z0-9/]+)(\[([^]]*)\])*$`', $valid_element, $matches)) {
|
||||
// Element names can be specified by the special character "@" (used
|
||||
// to allow a common set of attributes for all valid HTML elements)
|
||||
// or a list of names separated by the special character "/".
|
||||
$elements = array_unique(array_filter(explode('/', $matches[1])));
|
||||
|
||||
// Parse allowed attributes list (empty list means no attributes are allowed).
|
||||
$attributes = array();
|
||||
if (!empty($matches[3])) {
|
||||
// More than one attribute can be specified in the list (separator: "|").
|
||||
foreach (array_filter(explode('|', $matches[3])) as $attribute) {
|
||||
// Split item into attribute name and (optional) attribute options.
|
||||
$attribute_options = array();
|
||||
if (preg_match('`^([-a-z]+)([=:<].*)$`', $attribute, $match)) {
|
||||
$attribute = $match[1];
|
||||
|
||||
// Parse attribute options.
|
||||
if (strpos('=:<', $match[2][0]) !== FALSE) {
|
||||
$operator = $match[2][0];
|
||||
if ($operator == '=') {
|
||||
// Default value for the attribute (applied when present without explicit value).
|
||||
$attribute_options['default'] = substr($match[2], 1);
|
||||
}
|
||||
else if ($operator == ':') {
|
||||
// Forced value for the attribute (applied when present regardless of the specified value).
|
||||
$attribute_options['forced'] = substr($match[2], 1);
|
||||
}
|
||||
else if ($operator == '<') {
|
||||
// This attribute accepts only the specified list of values (separator: "?").
|
||||
$attribute_options['values'] = array_unique(array_filter(explode('?', substr($match[2], 1))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Are all attributes allowed for this element?
|
||||
if ($attribute == '*') {
|
||||
$attributes['*'] = array();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($attribute, 0, 1) == '!') {
|
||||
// If this attribute is not present in parsed HTML, then
|
||||
// the whole HTML element will be stripped out.
|
||||
$attribute = substr($attribute, 1);
|
||||
$attribute_options['required'] = TRUE;
|
||||
}
|
||||
|
||||
// Ignore malformed attribute names.
|
||||
if (!preg_match('`^[a-z][-a-z]*$`', $attribute)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attributes related to DOM events (on*) are not allowed here.
|
||||
if (substr($attribute, 0, 2) == 'on') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect attribute options.
|
||||
if (!isset($attributes[$attribute])) {
|
||||
$attributes[$attribute] = array();
|
||||
}
|
||||
foreach ($attribute_options as $option_type => $option_value) {
|
||||
$attributes[$attribute][$option_type] = $option_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain list of element names/synonyms (separated by /).
|
||||
// Consider synonyms as different elements with same exact attributes.
|
||||
foreach ($elements as $element) {
|
||||
if ($element == '@') {
|
||||
// These attributes should be enabled for all elements.
|
||||
foreach ($attributes as $attribute => $attribute_options) {
|
||||
if (!isset($common_attributes[$attribute])) {
|
||||
$common_attributes[$attribute] = array();
|
||||
}
|
||||
foreach ($attribute_options as $option_type => $option_value) {
|
||||
$common_attributes[$attribute][$option_type] = $option_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Ignore element name prefixes (+ - #) that are allowed for the
|
||||
// TynyMCE valid_elements parameter, but for the sake of simplicity,
|
||||
// our server side filter ignores them.
|
||||
if (strpos('+-#', $element[0]) !== FALSE) {
|
||||
$element = substr($element, 1);
|
||||
}
|
||||
if (!isset($parsed_elements[$element])) {
|
||||
$parsed_elements[$element] = array();
|
||||
}
|
||||
if (!isset($parsed_elements[$element]['*'])) {
|
||||
foreach ($attributes as $attribute => $attribute_options) {
|
||||
if ($attribute == '*') {
|
||||
$parsed_elements[$element] = array('*' => array());
|
||||
break;
|
||||
}
|
||||
if (!isset($parsed_elements[$element][$attribute])) {
|
||||
$parsed_elements[$element][$attribute] = array();
|
||||
}
|
||||
foreach ($attribute_options as $option_type => $option_value) {
|
||||
$parsed_elements[$element][$attribute][$option_type] = $option_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append commonly allowed attributes to each allowed element.
|
||||
if (!empty($common_attributes)) {
|
||||
foreach ($parsed_elements as $element => &$attributes) {
|
||||
// Do not append common attributes when all are allowed.
|
||||
if (isset($parsed_elements[$element]['*'])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($common_attributes as $attribute => $attribute_options) {
|
||||
if (!isset($attributes[$attribute])) {
|
||||
$attributes[$attribute] = array();
|
||||
}
|
||||
foreach ($attribute_options as $option_type => $option_value) {
|
||||
$attributes[$attribute][$option_type] = $option_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort HTML elements alphabetically (for filter tips).
|
||||
ksort($parsed_elements);
|
||||
|
||||
return $parsed_elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain list of style properties along their syntax rules.
|
||||
*
|
||||
* Style property information in compiled in logical groups, so it's
|
||||
* easier to build the filter settings form.
|
||||
*
|
||||
* Note that regular expression quantifiers are limited by number
|
||||
* of digits/characters. This is to prevent users from posting
|
||||
* big numbers/strings in property values that could cause browsers
|
||||
* to crash due to overflows, or any other kind of issue. Note that
|
||||
* users will still be able to break page layouts when using certain
|
||||
* combinations of numbers and units (ie. 100em, etc.), but nothing
|
||||
* more than that, hopefully.
|
||||
* This kind of issues may also happen when validating HTML attributes
|
||||
* where values are just checked for bad protocols. This is the same
|
||||
* exact measure taken by Drupal's filter_xss(), which has been a
|
||||
* friend of us for a long time. It's a matter of balance between
|
||||
* performance, code complexity, etc.
|
||||
*
|
||||
* All regular expressions are aimed to be delimited by `^ and $`.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see wysiwyg_filter_get_style_properties()
|
||||
* @see wysiwyg_filter_settings_filter()
|
||||
*/
|
||||
function wysiwyg_filter_get_style_property_groups() {
|
||||
$regexp_integer = '[-]?[0-9]{1,3}';
|
||||
$regexp_number = '[-]?(?:[0-9]{0,3}|[0-9]{0,3}\.[0-9]{1,4})';
|
||||
$regexp_length = $regexp_number . '(?:px|pt|em|ex|in|cm|mm|pc)?';
|
||||
$regexp_percent = '[-]?[12]?[0-9]{1,2}%';
|
||||
$regexp_color = '#[a-fA-F0-9]{3}|#[a-fA-F0-9]{6}|rgb\(\s*[0-9]{0,3}%?(?:\s*,\s*[0-9]{0,3}%?){2}\s*\)|[a-zA-Z]+';
|
||||
$regexp_bgcolor = $regexp_color . '|transparent';
|
||||
$regexp_border_width = $regexp_length . '|thin|medium|thick';
|
||||
$regexp_border_style = 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset';
|
||||
$regexp_uri = 'url\(\s*[\'"]?(?:[^)]|(?<=\\\\)\\))+[\'"]?\s*\)';
|
||||
$regexp_shape = 'rect\(\s*(?:auto|' . $regexp_length . ')(?:\s+(?:auto|' . $regexp_length . ')){3}\s*\)';
|
||||
$regexp_list_style_type = 'none|disc|circle|square|decimal(?:-leading-zero)?|lower-(?:alpha|greek|latin|roman)|upper-(?:alpha|latin|roman)|hebrew|armenian|georgian|cjk-ideographic|hiragana(?:-iroha)?|katakana(?:-iroha)?';
|
||||
|
||||
// Note that shorthand properties such as background and font are built
|
||||
// below, so we can reuse regular expression definitions.
|
||||
$groups = array(
|
||||
'color' => array(
|
||||
'title' => t('Color and background properties'),
|
||||
'properties' => array(
|
||||
'color' => '(?:' . $regexp_color . ')',
|
||||
'background' => '', // See this property expanded below.
|
||||
'background-color' => '(?:' . $regexp_bgcolor . ')',
|
||||
'background-image' => '(?:none|' . $regexp_uri . ')',
|
||||
'background-repeat' => '(?:no-repeat|repeat(?:-x|-y)?)',
|
||||
'background-attachment' => '(?:scroll|fixed)',
|
||||
'background-position' => '(?:(?:(?:top|center|bottom|left|right)(?:\s+(?:top|center|bottom|left|right))?)|(?:(?:' . $regexp_length . '|' . $regexp_percent . ')(?:\s+(?:' . $regexp_length . '|' . $regexp_percent . '))?))',
|
||||
),
|
||||
),
|
||||
'font' => array(
|
||||
'title' => t('Font properties'),
|
||||
'properties' => array(
|
||||
'font' => '', // See this property expanded below.
|
||||
'font-family' => '(?:[-_a-zA-Z0-9"\' ]*(?:\s*,\s*[-_a-zA-Z0-9"\' ]*)*)',
|
||||
'font-size' => '(?:(?:x-|xx-)?(?:small|large)(?:er)?|medium|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'font-size-adjust' => '(?:none|' . $regexp_number . ')',
|
||||
'font-stretch' => '(?:normal|wider|narrower|(?:ultra-|extra-|semi-)?(?:condensed|expanded))',
|
||||
'font-style' => '(?:normal|italic|oblique)',
|
||||
'font-variant' => '(?:normal|small-caps)',
|
||||
'font-weight' => '(?:normal|bold|bolder|lighter|[1-9]00)',
|
||||
),
|
||||
),
|
||||
'text' => array(
|
||||
'title' => t('Text properties'),
|
||||
'properties' => array(
|
||||
'text-align' => '(?:left|right|center|justify)',
|
||||
'text-decoration' => '(?:none|underline|overline|line-through|blink)',
|
||||
'text-indent' => '(?:' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'text-transform' => '(?:none|capitalize|(?:upper|lower)case)',
|
||||
'letter-spacing' => '(?:normal|' . $regexp_length . ')',
|
||||
'word-spacing' => '(?:normal|' . $regexp_length . ')',
|
||||
'white-space' => '(?:normal|pre|nowrap)',
|
||||
'direction' => '(?:ltr|rtl)',
|
||||
'unicode-bidi' => '(?:normal|embed|bidi-override)',
|
||||
),
|
||||
),
|
||||
'box' => array(
|
||||
'title' => t('Box properties'),
|
||||
'properties' => array(
|
||||
'margin' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')(?:\s+(?:auto|' . $regexp_length . '|' . $regexp_percent . ')){0,3}',
|
||||
'margin-top' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'margin-right' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'margin-bottom' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'margin-left' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'padding' => '(?:' . $regexp_length . '|' . $regexp_percent . ')(?:\s+(?:' . $regexp_length . '|' . $regexp_percent . ')){0,3}',
|
||||
'padding-top' => '(?:' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'padding-right' => '(?:' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'padding-bottom' => '(?:' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'padding-left' => '(?:' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
),
|
||||
),
|
||||
'border-1' => array(
|
||||
'title' => t('Border properties (1)'),
|
||||
'properties' => array(
|
||||
'border' => '(?:(?:' . $regexp_border_width . ')?(?:\s*(?:' . $regexp_border_style . ')?(?:\s*(?:' . $regexp_bgcolor . ')?)))',
|
||||
'border-top' => '(?:(?:' . $regexp_border_width . ')?(?:\s*(?:' . $regexp_border_style . ')?(?:\s*(?:' . $regexp_bgcolor . ')?)))',
|
||||
'border-right' => '(?:(?:' . $regexp_border_width . ')?(?:\s*(?:' . $regexp_border_style . ')?(?:\s*(?:' . $regexp_bgcolor . ')?)))',
|
||||
'border-bottom' => '(?:(?:' . $regexp_border_width . ')?(?:\s*(?:' . $regexp_border_style . ')?(?:\s*(?:' . $regexp_bgcolor . ')?)))',
|
||||
'border-left' => '(?:(?:' . $regexp_border_width . ')?(?:\s*(?:' . $regexp_border_style . ')?(?:\s*(?:' . $regexp_bgcolor . ')?)))',
|
||||
'border-width' => '(?:' . $regexp_border_width . ')(?:\s+(?:' . $regexp_border_width . ')){0,3}',
|
||||
'border-top-width' => '(?:' . $regexp_border_width . ')',
|
||||
'border-right-width' => '(?:' . $regexp_border_width . ')',
|
||||
'border-bottom-width' => '(?:' . $regexp_border_width . ')',
|
||||
'border-left-width' => '(?:' . $regexp_border_width . ')',
|
||||
),
|
||||
),
|
||||
'border-2' => array(
|
||||
'title' => t('Border properties (2)'),
|
||||
'properties' => array(
|
||||
'border-color' => '(?:' . $regexp_bgcolor . ')(?:\s+(?:' . $regexp_bgcolor . ')){0,3}',
|
||||
'border-top-color' => '(?:' . $regexp_bgcolor . ')',
|
||||
'border-right-color' => '(?:' . $regexp_bgcolor . ')',
|
||||
'border-bottom-color' => '(?:' . $regexp_bgcolor . ')',
|
||||
'border-left-color' => '(?:' . $regexp_bgcolor . ')',
|
||||
'border-style' => '(?:' . $regexp_border_style . ')(?:\s+(?:' . $regexp_border_style . ')){0,3}',
|
||||
'border-top-style' => '(?:' . $regexp_border_style . ')',
|
||||
'border-right-style' => '(?:' . $regexp_border_style . ')',
|
||||
'border-bottom-style' => '(?:' . $regexp_border_style . ')',
|
||||
'border-left-style' => '(?:' . $regexp_border_style . ')',
|
||||
),
|
||||
),
|
||||
'dimension' => array(
|
||||
'title' => t('Dimension properties'),
|
||||
'properties' => array(
|
||||
'height' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'line-height' => '(?:normal|' . $regexp_number . '|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'max-height' => '(?:none|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'max-width' => '(?:none|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'min-height' => '(?:' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'min-width' => '(?:' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'width' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
),
|
||||
),
|
||||
'positioning' => array(
|
||||
'title' => t('Positioning properties'),
|
||||
'properties' => array(
|
||||
'bottom' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'clip' => '(?:auto|' . $regexp_shape . ')',
|
||||
'left' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'overflow' => '(?:visible|hidden|scroll|auto)',
|
||||
'right' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'top' => '(?:auto|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'vertical-align' => '(?:baseline|sub|super|middle|(?:text-)?(?:top|bottom)|' . $regexp_length . '|' . $regexp_percent . ')',
|
||||
'z-index' => '(?:auto|' . $regexp_integer . ')',
|
||||
),
|
||||
),
|
||||
'layout' => array(
|
||||
'title' => t('Layout properties'),
|
||||
'properties' => array(
|
||||
'clear' => '(?:left|right|both|none)',
|
||||
'display' => '(?:none|inline|block|list-item|run-in|compact|marker|table-(?:(?:row|header|group|column)-group|row|column|cell|caption)|(?:inline-)?table)',
|
||||
'float' => '(?:left|right|none)',
|
||||
'position' => '(?:static|relative|absolute|fixed)',
|
||||
'visibility' => '(?:visible|hidden|collapse)',
|
||||
),
|
||||
),
|
||||
'list' => array(
|
||||
'title' => t('List properties'),
|
||||
'properties' => array(
|
||||
'list-style' => '(?:(?:' . $regexp_list_style_type . ')?(?:\s*(?:(?:in|out)side)?(?:\s*(?:none|' . $regexp_uri . ')?)))',
|
||||
'list-style-image' => '(?:none|' . $regexp_uri . ')',
|
||||
'list-style-position' => '(?:inside|outside)',
|
||||
'list-style-type' => '(?:' . $regexp_list_style_type . ')',
|
||||
),
|
||||
),
|
||||
'table' => array(
|
||||
'title' => t('Table properties'),
|
||||
'properties' => array(
|
||||
'border-collapse' => '(?:collapse|separate)',
|
||||
'border-spacing' => '(?:' . $regexp_length . '(?:\s+' . $regexp_length . ')?)',
|
||||
'caption-side' => '(?:top|bottom|left|right)',
|
||||
'empty-cells' => '(?:show|hide)',
|
||||
'table-layout' => '(?:auto|fixed)',
|
||||
),
|
||||
),
|
||||
'user' => array(
|
||||
'title' => t('User interface properties'),
|
||||
'properties' => array(
|
||||
'cursor' => '(?:auto|crosshair|default|pointer|move|(?:e|ne|nw|n|se|sw|s|w)-resize|text|wait|help)',
|
||||
'outline' => '(?:(?:' . $regexp_color . '|invert)?(?:\s*(?:' . $regexp_border_style . ')?(?:\s*(?:' . $regexp_border_width . ')?)))',
|
||||
'outline-width' => '(?:' . $regexp_border_width . ')',
|
||||
'outline-style' => '(?:' . $regexp_border_style . ')',
|
||||
'outline-color' => '(?:' . $regexp_color . '|invert)',
|
||||
'zoom' => '(?:normal|' . $regexp_number . '|' . $regexp_percent . ')',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// 'background' property.
|
||||
$regexp = '(?:' .
|
||||
$groups['color']['properties']['background-color'] . '|' .
|
||||
$groups['color']['properties']['background-image'] . '|' .
|
||||
$groups['color']['properties']['background-repeat'] . '|' .
|
||||
$groups['color']['properties']['background-attachment'] . '|' .
|
||||
$groups['color']['properties']['background-position'] . ')';
|
||||
$groups['color']['properties']['background'] = '(?:' . $regexp . ')(?:(?:\s+' . $regexp . ')+)';
|
||||
|
||||
// 'font' property.
|
||||
$regexp = '(?:' .
|
||||
$groups['font']['properties']['font-style'] . '|' .
|
||||
$groups['font']['properties']['font-variant'] . '|' .
|
||||
$groups['font']['properties']['font-weight'] . '|' .
|
||||
'(?:' . $groups['font']['properties']['font-size'] . ')(?:\s*/\s*' . $groups['dimension']['properties']['line-height'] . ')?|' .
|
||||
$groups['font']['properties']['font-family'] . ')';
|
||||
$groups['font']['properties']['font'] = '(?:' . $regexp . ')(?:(?:\s+' . $regexp . ')+)';
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filter options.
|
||||
*
|
||||
* @param int $format_name
|
||||
* Input format identifier.
|
||||
* @param int $settings
|
||||
* Filter settings.
|
||||
* @return array
|
||||
*/
|
||||
function wysiwyg_filter_get_filter_options($format_name, $settings) {
|
||||
$filter_options = array(
|
||||
'valid_elements' => wysiwyg_filter_parse_valid_elements($settings['valid_elements']),
|
||||
'allow_comments' => $settings['allow_comments'],
|
||||
'style_properties' => wysiwyg_filter_get_style_properties($format_name, $settings),
|
||||
'nofollow_policy' => $settings['nofollow_policy'],
|
||||
'nofollow_domains' => $settings['nofollow_domains'],
|
||||
);
|
||||
foreach (wysiwyg_filter_get_advanced_rules() as $rule_key => $rule_info) {
|
||||
$filter_options[$rule_key] = array();
|
||||
foreach ($settings["rule_$rule_key"] as $rule) {
|
||||
$filter_options[$rule_key][] = '`^' . str_replace("\xFF", $rule_info['asterisk_expansion'], preg_quote(str_replace('*', "\xFF", $rule), '`')) . '$`';
|
||||
}
|
||||
}
|
||||
return $filter_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed style properties.
|
||||
*
|
||||
* @param int $format_name
|
||||
* Input format identifier.
|
||||
* @param int $settings
|
||||
* Filter settings.
|
||||
* @return array
|
||||
*/
|
||||
function wysiwyg_filter_get_style_properties($format_name, $settings) {
|
||||
static $style_properties = array();
|
||||
if (!isset($style_properties[$format_name])) {
|
||||
$style_properties[$format_name] = array();
|
||||
foreach (wysiwyg_filter_get_style_property_groups() as $group => $group_info) {
|
||||
$allowed_styles = array_filter($settings["style_$group"]);
|
||||
foreach ($group_info['properties'] as $property => $regexp) {
|
||||
if (isset($allowed_styles[$property])) {
|
||||
$style_properties[$format_name][$property] = '`^' . $regexp . '$`';
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort style properties alphabetically (for filter tips).
|
||||
ksort($style_properties[$format_name]);
|
||||
}
|
||||
return $style_properties[$format_name];
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
name = WYSIWYG Filter
|
||||
description = Provides an input filter that allows site administrators configure which HTML elements, attributes and style properties are allowed.
|
||||
package = Input filters
|
||||
core = 7.x
|
||||
|
||||
files[] = wysiwyg_filter.admin.inc
|
||||
files[] = wysiwyg_filter.inc
|
||||
files[] = wysiwyg_filter.install
|
||||
files[] = wysiwyg_filter.module
|
||||
files[] = wysiwyg_filter.pages.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-10-26
|
||||
version = "7.x-1.6-rc2+0-dev"
|
||||
core = "7.x"
|
||||
project = "wysiwyg_filter"
|
||||
datestamp = "1382797388"
|
||||
|
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Module installation/uninstallation hooks.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function wysiwyg_filter_uninstall() {
|
||||
// Delete all module variables and then clear the variables cache.
|
||||
// TODO Please review the conversion of this statement to the D7 database API syntax.
|
||||
/* db_query("DELETE FROM {variable} WHERE name LIKE 'wysiwyg\_filter\_%'") */
|
||||
db_delete('variable')
|
||||
->condition('name', 'wysiwyg\_filter\_%', 'LIKE')
|
||||
->execute();
|
||||
cache_clear_all('variables', 'cache');
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides an input filter that allows site administrators configure which
|
||||
* HTML elements, attributes and style properties are allowed.
|
||||
*/
|
||||
|
||||
// todo: more intelligent include policy
|
||||
// beware: it filter process function in ...page.inc is not included,
|
||||
// the whole filter is silently ignored. see http://drupal.org/node/1151506
|
||||
module_load_include('inc', 'wysiwyg_filter', 'wysiwyg_filter.admin');
|
||||
module_load_include('inc', 'wysiwyg_filter', 'wysiwyg_filter.pages');
|
||||
|
||||
/**
|
||||
* Implements hook_filter_info().
|
||||
*/
|
||||
function wysiwyg_filter_filter_info() {
|
||||
// Load common functions.
|
||||
module_load_include('inc', 'wysiwyg_filter');
|
||||
$filters = array();
|
||||
global $base_url;
|
||||
$parts = parse_url($base_url);
|
||||
|
||||
$defaults = array(
|
||||
'valid_elements' => wysiwyg_filter_default_valid_elements(),
|
||||
'allow_comments' => 0,
|
||||
'nofollow_policy' => 'whitelist',
|
||||
'nofollow_domains' => array($parts['host']),
|
||||
);
|
||||
foreach(wysiwyg_filter_get_style_property_groups() as $group => $stuff):
|
||||
$defaults["style_$group"] = array();
|
||||
endforeach;
|
||||
foreach(wysiwyg_filter_get_advanced_rules() as $rule => $stuff):
|
||||
$defaults["rule_$rule"] = array();
|
||||
endforeach;
|
||||
|
||||
$filters['wysiwyg'] = array(
|
||||
'title' => t('WYSIWYG Filter'),
|
||||
'description' => t('Allows you to restrict whether users can post HTML and which tags and attributes per HTML tag to filter out.'),
|
||||
'process callback' => 'wysiwyg_filter_filter_wysiwyg_process',
|
||||
'settings callback' => 'wysiwyg_filter_filter_wysiwyg_settings',
|
||||
'tips callback' => 'wysiwyg_filter_filter_wysiwyg_tips',
|
||||
'default settings' => $defaults
|
||||
);
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_filter_FILTER_tips().
|
||||
*/
|
||||
function wysiwyg_filter_filter_wysiwyg_tips($filter, $format, $long = FALSE) {
|
||||
// Load common functions.
|
||||
module_load_include('inc', 'wysiwyg_filter');
|
||||
|
||||
$filter_options = wysiwyg_filter_get_filter_options($format->format, $filter->settings);
|
||||
$tips = array();
|
||||
if (!empty($filter_options['valid_elements'])) {
|
||||
$tags = array();
|
||||
foreach (array_keys($filter_options['valid_elements']) as $tag) {
|
||||
$tags[] = '<' . $tag . '>';
|
||||
}
|
||||
$tips[] = t('Allowed HTML tags: @tags', array('@tags' => implode(' ', $tags)));
|
||||
}
|
||||
if (!empty($filter_options['style_properties'])) {
|
||||
$tips[] = t('Allowed Style properties: @properties', array('@properties' => implode(', ', array_keys($filter_options['style_properties']))));
|
||||
}
|
||||
if (!empty($tips)) {
|
||||
return implode('<br />', $tips);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of wysiwyg_editor_settings_alter().
|
||||
*/
|
||||
function wysiwyg_filter_wysiwyg_editor_settings_alter(&$editor_settings, $context) {
|
||||
// Provide the valid_elements option to TinyMCE editors, only if the WYSIWYG
|
||||
// Filter is enabled in the input format related to the current given context.
|
||||
if ($context['profile']->editor == 'tinymce'):
|
||||
// first get the filters and their settings
|
||||
if (isset($context['profile']->format)):
|
||||
$format_name = $context['profile']->format;
|
||||
$filters = filter_list_format($format_name);
|
||||
if($filters && array_key_exists('wysiwyg', $filters)):
|
||||
$filter = $filters['wysiwyg'];
|
||||
if($filter->status != 0) {
|
||||
$settings = $filter->settings;
|
||||
$editor_settings['valid_elements'] = preg_replace('#\s+#', '', $settings['valid_elements']);
|
||||
}
|
||||
endif;
|
||||
endif;
|
||||
endif;
|
||||
}
|
@@ -0,0 +1,518 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* User land code for the WYSIWYG Filter module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* WYSIWYG Filter. Provides filtering of input into accepted HTML.
|
||||
*
|
||||
* This function is based on Drupal's filter_xss() with a few additions:
|
||||
* - Validates HTML input against whitelists of HTML elements, attributes
|
||||
* and style properties.
|
||||
* - Optionally apply rel="nofollow" rules to links.
|
||||
* - Rules for the above can be specified by site administrators from the
|
||||
* filter settings form.
|
||||
*
|
||||
* @param string $text
|
||||
* HTML text to be filtered.
|
||||
* @param int $format
|
||||
* Input format identifier.
|
||||
* @return string
|
||||
* Filtered HTML text.
|
||||
*/
|
||||
function wysiwyg_filter_filter_wysiwyg_process($text, $filter, $format, $langcode, $cache, $cache_id) {
|
||||
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
|
||||
// site scripting issues on Internet Explorer 6.
|
||||
if (!drupal_validate_utf8($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Load common functions.
|
||||
module_load_include('inc', 'wysiwyg_filter');
|
||||
|
||||
// Store input filter options.
|
||||
_wysiwyg_filter_xss_split(wysiwyg_filter_get_filter_options($format->format, $filter->settings), TRUE);
|
||||
|
||||
// Remove NUL characters (ignored by some browsers).
|
||||
$text = str_replace(chr(0), '', $text);
|
||||
// Remove Netscape 4 JS entities.
|
||||
$text = preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $text);
|
||||
|
||||
// Defuse all HTML entities.
|
||||
$text = str_replace('&', '&', $text);
|
||||
// Change back only well-formed entities in our whitelist
|
||||
// Decimal numeric entities.
|
||||
$text = preg_replace('/&#([0-9]+;)/', '&#\1', $text);
|
||||
// Hexadecimal numeric entities.
|
||||
$text = preg_replace('/&#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/', '&#x\1', $text);
|
||||
// Named entities.
|
||||
$text = preg_replace('/&([A-Za-z][A-Za-z0-9]*;)/', '&\1', $text);
|
||||
|
||||
return preg_replace_callback('%
|
||||
(
|
||||
<(?=[^a-zA-Z!/]) # a lone <
|
||||
| # or
|
||||
<!--.*?--> # a comment
|
||||
| # or
|
||||
<[^>]*(>|$) # a string that starts with a <, up until the > or the end of the string
|
||||
| # or
|
||||
> # just a >
|
||||
)%x', '_wysiwyg_filter_xss_split', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an HTML tag.
|
||||
*
|
||||
* @param $m
|
||||
* An array with various meaning depending on the value of $store.
|
||||
* If $store is TRUE then the array contains the allowed tags.
|
||||
* If $store is FALSE then the array has one element, the HTML tag to process.
|
||||
* @param $store
|
||||
* Whether to store $m.
|
||||
* @return
|
||||
* If the element isn't allowed, an empty string. Otherwise, the cleaned up
|
||||
* version of the HTML element.
|
||||
*/
|
||||
function _wysiwyg_filter_xss_split($m, $store = FALSE) {
|
||||
static $filter_options;
|
||||
|
||||
if ($store) {
|
||||
_wysiwyg_filter_xss_attributes($filter_options = $m);
|
||||
return;
|
||||
}
|
||||
|
||||
$string = $m[1];
|
||||
|
||||
if (substr($string, 0, 1) != '<') {
|
||||
// We matched a lone ">" character
|
||||
return '>';
|
||||
}
|
||||
else if (strlen($string) == 1) {
|
||||
// We matched a lone "<" character
|
||||
return '<';
|
||||
}
|
||||
|
||||
if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
|
||||
// Seriously malformed
|
||||
return '';
|
||||
}
|
||||
|
||||
$slash = trim($matches[1]);
|
||||
$elem = strtolower($matches[2]);
|
||||
$attrlist = &$matches[3];
|
||||
$comment = &$matches[4];
|
||||
|
||||
if (!empty($comment)) {
|
||||
// Allow or disallow HTML comments.
|
||||
return (!empty($filter_options['allow_comments']) ? $comment : '');
|
||||
}
|
||||
elseif (!isset($filter_options['valid_elements'][$elem])) {
|
||||
// Disallowed HTML element.
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($slash != '') {
|
||||
return "</$elem>";
|
||||
}
|
||||
|
||||
// Is there a closing XHTML slash at the end of the attributes?
|
||||
// In PHP 5.1.0+ we could count the changes, currently we need a separate match
|
||||
$xhtml_slash = preg_match('%\s?/\s*$%', $attrlist) ? ' /' : '';
|
||||
$attrlist = preg_replace('%(\s?)/\s*$%', '\1', $attrlist);
|
||||
|
||||
// Clean up attributes
|
||||
if (($attr2 = _wysiwyg_filter_xss_attributes($attrlist, $elem)) === FALSE) {
|
||||
// Disallowed HTML element because it does not contain required attribute.
|
||||
return '';
|
||||
}
|
||||
$attr2 = implode(' ', $attr2);
|
||||
$attr2 = preg_replace('/[<>]/', '', $attr2);
|
||||
$attr2 = strlen($attr2) ? ' ' . $attr2 : '';
|
||||
|
||||
return "<$elem$attr2$xhtml_slash>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a string of HTML attributes.
|
||||
*
|
||||
* @param mixed $attr
|
||||
* String with attributes list to be checked.
|
||||
* Array with whitelist of all HTML elements and their allowed attributes.
|
||||
* @param string $element
|
||||
* Current element for specified attributes lists.
|
||||
* @return
|
||||
* Cleaned up version of the HTML attributes.
|
||||
*/
|
||||
function _wysiwyg_filter_xss_attributes($attr, $element = '') {
|
||||
static $filter_options;
|
||||
|
||||
if (is_array($attr)) {
|
||||
$filter_options = $attr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Shortcuts for filter options.
|
||||
$allowed_attributes = &$filter_options['valid_elements'][$element];
|
||||
$allowed_properties = &$filter_options['style_properties'];
|
||||
$allowed_style_urls = &$filter_options['style_urls'];
|
||||
$allowed_class_names = &$filter_options['valid_classes'];
|
||||
$allowed_element_ids = &$filter_options['valid_ids'];
|
||||
$nofollow_policy = &$filter_options['nofollow_policy'];
|
||||
$nofollow_domains = &$filter_options['nofollow_domains'];
|
||||
|
||||
$attrarr = array();
|
||||
$mode = 0;
|
||||
$attrname = '';
|
||||
|
||||
while (strlen($attr) != 0) {
|
||||
// Was the last operation successful?
|
||||
$working = 0;
|
||||
|
||||
switch ($mode) {
|
||||
case 0:
|
||||
// Attribute name, href for instance.
|
||||
if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
|
||||
$attrname = strtolower($match[1]);
|
||||
$skip = (substr($attrname, 0, 2) == 'on' || (!isset($allowed_attributes[$attrname]) && !isset($allowed_attributes['*'])));
|
||||
$working = $mode = 1;
|
||||
$attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Equals sign or valueless ("selected").
|
||||
if (preg_match('/^\s*=\s*/', $attr)) {
|
||||
$working = 1;
|
||||
$mode = 2;
|
||||
$attr = preg_replace('/^\s*=\s*/', '', $attr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (preg_match('/^\s+/', $attr)) {
|
||||
$working = 1;
|
||||
$mode = 0;
|
||||
if (!$skip) {
|
||||
$attrarr[$attrname] = array();
|
||||
}
|
||||
$attr = preg_replace('/^\s+/', '', $attr);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Attribute value, a URL after href= for instance.
|
||||
if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match)) {
|
||||
if (!$skip) {
|
||||
$attrarr[$attrname] = array(
|
||||
'value' => $match[1],
|
||||
'delimiter' => '"',
|
||||
);
|
||||
}
|
||||
$working = 1;
|
||||
$mode = 0;
|
||||
$attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (preg_match("/^'([^']*)'(\s+|$)/", $attr, $match)) {
|
||||
if (!$skip) {
|
||||
$attrarr[$attrname] = array(
|
||||
'value' => $match[1],
|
||||
'delimiter' => '\'',
|
||||
);
|
||||
}
|
||||
$working = 1;
|
||||
$mode = 0;
|
||||
$attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (preg_match("%^([^\s\"']+)(\s+|$)%", $attr, $match)) {
|
||||
if (!$skip) {
|
||||
$attrarr[$attrname] = array(
|
||||
'value' => $match[1],
|
||||
'delimiter' => '"',
|
||||
);
|
||||
}
|
||||
$working = 1;
|
||||
$mode = 0;
|
||||
$attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($working == 0) {
|
||||
// not well formed, remove and try again.
|
||||
$attr = preg_replace('/
|
||||
^
|
||||
(
|
||||
"[^"]*("|$) # - a string that starts with a double quote, up until the next double quote or the end of the string
|
||||
| # or
|
||||
\'[^\']*(\'|$)| # - a string that starts with a quote, up until the next quote or the end of the string
|
||||
| # or
|
||||
\S # - a non-whitespace character
|
||||
)* # any number of the above three
|
||||
\s* # any number of whitespaces
|
||||
/x', '', $attr);
|
||||
$mode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The attribute list ends with a valueless attribute like "selected".
|
||||
if ($mode == 1 && !$skip) {
|
||||
$attrarr[$attrname] = array();
|
||||
}
|
||||
|
||||
// Check the current HTML element for required attributes.
|
||||
foreach ($allowed_attributes as $attrname => $attrinfo) {
|
||||
if (!empty($attrinfo['required']) && !isset($attrarr[$attrname])) {
|
||||
// Ignore the whole element if required attribute is not present.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// When no attribute value has been specified in parsed HTML stream,
|
||||
// then supply default value if provided by input format settings.
|
||||
if (!isset($attrinfo['value']) && isset($allowed_attributes[$attrname]['default'])) {
|
||||
$attrarr[$attrname] = array(
|
||||
'value' => $allowed_attributes[$attrname]['default'],
|
||||
'delimiter' => '"',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the current HTML element for additional attribute rules.
|
||||
$parsed_attributes = array();
|
||||
$add_nofollow = FALSE;
|
||||
foreach ($attrarr as $attrname => $attrinfo) {
|
||||
$parsed_attribute = $attrname;
|
||||
$attribute_options = (isset($allowed_attributes[$attrname]) ? $allowed_attributes[$attrname] : array());
|
||||
|
||||
if (isset($attrinfo['value'])) {
|
||||
// Supply forced attribute value as defined by input format?
|
||||
if (isset($attribute_options['forced'])) {
|
||||
$attrinfo['value'] = $attribute_options['forced'];
|
||||
}
|
||||
else if (isset($attribute_options['values']) && !in_array($attrinfo['value'], $attribute_options['values'])) {
|
||||
// Ignore attribute if value is not present in whitelist.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Additional validation of attribute values.
|
||||
if ($attrname == 'style') {
|
||||
// Ok, let us validate individual style properties (decode entities now).
|
||||
$dirty_properties = array_filter(array_map('trim', explode(';', decode_entities($attrinfo['value']))));
|
||||
$sanitized_properties = array();
|
||||
foreach ($dirty_properties as $dirty_property) {
|
||||
// Separate property name from its value.
|
||||
if (!preg_match('#^([a-zA-Z][-a-zA-Z]*)\s*:\s*(.*)$#', $dirty_property, $property_matches)) {
|
||||
// Ignore properties that do not match the format "property-name: value".
|
||||
continue;
|
||||
}
|
||||
$property_name = strtolower($property_matches[1]);
|
||||
$property_value = &$property_matches[2];
|
||||
if (!isset($allowed_properties[$property_name])) {
|
||||
// Ignore property if not whitelisted in filter settings.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check style property syntax.
|
||||
if (!preg_match($allowed_properties[$property_name], $property_value)) {
|
||||
// Ignore property if value does not match syntax rules.
|
||||
continue;
|
||||
}
|
||||
|
||||
// If property value comes with url(...), then we want to check if it's allowed or not.
|
||||
if (strpos($property_value, 'url(') !== FALSE) {
|
||||
if (count($allowed_style_urls) <= 0) {
|
||||
// Ignore property if no rules have been specified.
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is like $regexp_uri in wysiwyg_filter_get_style_property_groups(), but it now contains 2 capturing
|
||||
// groups [1] for the URL itself (including delimiters) and [2] the first delimiter (if any).
|
||||
if (!preg_match('`url\(\s*(([\'"]?)(?:[^)]|(?<=\\\\)\\))+[\'"]?)\s*\)`', $property_value, $url) || empty($url[1])) {
|
||||
// Ignore property if found to be malformed here.
|
||||
continue;
|
||||
}
|
||||
if (!empty($url[2])) {
|
||||
if (substr($url[1], -1) != $url[2]) {
|
||||
// Ignore property if start and end delimiters don't match.
|
||||
continue;
|
||||
}
|
||||
// Remove delimiters.
|
||||
$url[1] = substr($url[1], 1, -1);
|
||||
}
|
||||
// Remove backslashes that could have been used to escape parentheses,
|
||||
// commas, whitespace characters, single quotes or double quotes.
|
||||
// http://www.w3.org/TR/CSS2/syndata.html#uri
|
||||
$url = preg_replace('`\\\\([(),\'"\s])`', '\1', $url[1]);
|
||||
|
||||
// Ignore property if URL fails the check for bad protocols.
|
||||
if (wysiwyg_filter_xss_bad_protocol($url) != $url) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check URL against advanced filter rules.
|
||||
$match_found = FALSE;
|
||||
foreach ($allowed_style_urls as $regexp) {
|
||||
if (preg_match($regexp, $url)) {
|
||||
$match_found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$match_found) {
|
||||
// Ignore property if URL does not match any rule.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Filter property value for bad protocols (note that property value has already been decoded).
|
||||
$property_value = wysiwyg_filter_xss_bad_protocol($property_value);
|
||||
}
|
||||
|
||||
// Sanitized property name and value (check_plain'd here).
|
||||
$sanitized_properties[] = $property_name . ':' . check_plain($property_value);
|
||||
}
|
||||
|
||||
if (empty($sanitized_properties)) {
|
||||
// Ignore the whole style attribute if no property remains.
|
||||
continue;
|
||||
}
|
||||
|
||||
$attrinfo['value'] = implode('; ', $sanitized_properties);
|
||||
}
|
||||
else if ($attrname == 'class') {
|
||||
// Validate class names based on advanced rules specified in filter settings panel.
|
||||
// Note that property value is decoded now and check_plain'd at end. Since the colon
|
||||
// sign is not allowed, there's no need here to check for bad protocols.
|
||||
$dirty_names = array_filter(array_map('trim', explode(' ', decode_entities($attrinfo['value']))));
|
||||
$valid_names = array();
|
||||
foreach ($dirty_names as $dirty_name) {
|
||||
foreach ($allowed_class_names as $regexp) {
|
||||
if (preg_match($regexp, $dirty_name)) {
|
||||
$valid_names[] = $dirty_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($valid_names)) {
|
||||
// Ignore attribute if no class name remains after validation.
|
||||
continue;
|
||||
}
|
||||
$attrinfo['value'] = check_plain(implode(' ', $valid_names));
|
||||
}
|
||||
else if ($attrname == 'id') {
|
||||
// Validate element IDs based on advanced rules specified in filter settings panel.
|
||||
// Note that property value is decoded now and check_plain'd at end. Since the colon
|
||||
// sign is not allowed, there's no need here to check for bad protocols.
|
||||
if (count($allowed_element_ids) <= 0) {
|
||||
// Ignore attribute if no rules have been specified.
|
||||
continue;
|
||||
}
|
||||
// Decode value so we can easilly check it.
|
||||
$attrinfo['value'] = decode_entities($attrinfo['value']);
|
||||
// Pattern starts valid, but it should match all specified rules.
|
||||
$match_found = FALSE;
|
||||
foreach ($allowed_element_ids as $regexp) {
|
||||
if (preg_match($regexp, $attrinfo['value'])) {
|
||||
$match_found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$match_found) {
|
||||
// Ignore attribute if it contains invalid value.
|
||||
continue;
|
||||
}
|
||||
// Element ID is valid, check_plain result.
|
||||
$attrinfo['value'] = check_plain($attrinfo['value']);
|
||||
}
|
||||
else {
|
||||
// All attribute values are checked for bad protocols. This is the same
|
||||
// exact method used by Drupal's filter_xss().
|
||||
$attrinfo['value'] = filter_xss_bad_protocol($attrinfo['value']);
|
||||
|
||||
// If this is <a href> element, then check domain name for rel="nofollow" policies in effect.
|
||||
if ($element == 'a' && $attrname == 'href' && $nofollow_policy != 'disabled' && !$add_nofollow) {
|
||||
$domain_found = FALSE;
|
||||
foreach ($nofollow_domains as $domain) {
|
||||
$domain = str_replace('.', '\.', $domain); // escape dots
|
||||
if (preg_match('#://.*' . $domain . '([^a-z0-9]|$)#i', $attrinfo['value'])) {
|
||||
$domain_found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (($nofollow_policy == 'blacklist' && $domain_found) || ($nofollow_policy == 'whitelist' && !$domain_found)) {
|
||||
$add_nofollow = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build parsed attribute value.
|
||||
$parsed_attribute .= '=' . $attrinfo['delimiter'] . $attrinfo['value'] . $attrinfo['delimiter'];
|
||||
}
|
||||
|
||||
$parsed_attributes[$attrname] = $parsed_attribute;
|
||||
}
|
||||
|
||||
// Do we have a link where rel="nofollow" should be added?
|
||||
if ($add_nofollow) {
|
||||
if (empty($parsed_attributes['rel'])) {
|
||||
$parsed_attributes['rel'] = 'rel="nofollow"';
|
||||
}
|
||||
else if (strpos($parsed_attributes['rel'], 'nofollow') === FALSE) {
|
||||
// Since we know the attribute is well formed, we can use substr(), which is faster than preg_replace().
|
||||
$parsed_attributes['rel'] = substr($parsed_attributes['rel'], 0, -1) . ' nofollow' . substr($parsed_attributes['rel'], -1);
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed_attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an style property value and ensures it does not contain an URL
|
||||
* with a disallowed protocol (only http/https are allowed here).
|
||||
*
|
||||
* This function is based on Drupal's filter_xss_bad_protocol(). Differences are:
|
||||
* 1) It does not decode input string.
|
||||
* It should be done by the caller before calling us.
|
||||
* 2) It does not apply check_plain() to result.
|
||||
* It should be done by the caller after calling us.
|
||||
* 3) It allows a lot less protocols.
|
||||
*
|
||||
* @param $string
|
||||
* The string with the style property value.
|
||||
* @return
|
||||
* Cleaned up version of $string.
|
||||
*/
|
||||
function wysiwyg_filter_xss_bad_protocol($string) {
|
||||
$allowed_protocols = array(
|
||||
'http' => 1,
|
||||
'https' => 1,
|
||||
);
|
||||
|
||||
// Iteratively remove any invalid protocol found.
|
||||
|
||||
do {
|
||||
$before = $string;
|
||||
$colonpos = strpos($string, ':');
|
||||
if ($colonpos > 0) {
|
||||
// We found a colon, possibly a protocol. Verify.
|
||||
$protocol = substr($string, 0, $colonpos);
|
||||
// If a colon is preceded by a slash, question mark or hash, it cannot
|
||||
// possibly be part of the URL scheme. This must be a relative URL,
|
||||
// which inherits the (safe) protocol of the base document.
|
||||
if (preg_match('![/?#]!', $protocol)) {
|
||||
break;
|
||||
}
|
||||
// Per RFC2616, section 3.2.3 (URI Comparison) scheme comparison must be case-insensitive
|
||||
// Check if this is a disallowed protocol.
|
||||
if (!isset($allowed_protocols[strtolower($protocol)])) {
|
||||
$string = substr($string, $colonpos + 1);
|
||||
}
|
||||
}
|
||||
} while ($before != $string);
|
||||
return $string;
|
||||
}
|
Reference in New Issue
Block a user