FINAL suepr merge step : added all modules to this super repos
This commit is contained in:
274
sites/all/modules/contrib/content/auto_nodetitle/LICENSE.txt
Normal file
274
sites/all/modules/contrib/content/auto_nodetitle/LICENSE.txt
Normal file
@@ -0,0 +1,274 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
|
||||
Cambridge, MA 02139, 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 Library 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
|
75
sites/all/modules/contrib/content/auto_nodetitle/README.txt
Normal file
75
sites/all/modules/contrib/content/auto_nodetitle/README.txt
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
Automatic Nodetitle Module
|
||||
------------------------
|
||||
by Wolfgang Ziegler, nuppla@zites.net
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
This is a small and efficent module that allows hiding of the content title field in the form.
|
||||
To prevent empty content title fields it sets the title to the content type name or to an
|
||||
configurable string. It is possible to use various content data for the autogenerated title,
|
||||
e.g. the token [current-user:name] is going to be replaced with the currently logged in
|
||||
users name. If the token module is installed, a list of possible replacement patterns
|
||||
will be shown.
|
||||
|
||||
Advanced users can also provide some PHP code, that is used for automatically generating an
|
||||
appropriate title.
|
||||
|
||||
Installation
|
||||
------------
|
||||
* (optional) Download and install the token module in order to get token
|
||||
replacement help.
|
||||
* Copy the module's directory to your modules directory and activate the module.
|
||||
* For each content type you want to have an automatic title, configure the
|
||||
module at 'admin/structure/types'.
|
||||
|
||||
|
||||
Note
|
||||
-----
|
||||
Due to the way the module works, it is not possible to make use of some replacement
|
||||
tokens that are not available before the content node is saved the first time, e.g.
|
||||
like the node id ([node:nid]).
|
||||
|
||||
|
||||
|
||||
Advanced Use: PHP Code
|
||||
------------------------
|
||||
You can access $node from your php code. Look at this simple example, which just adds the node's
|
||||
author as title:
|
||||
|
||||
<?php return "Author: $node->name"; ?>
|
||||
|
||||
|
||||
|
||||
Advanced Use: Combining tokens and PHP
|
||||
---------------------------------------
|
||||
|
||||
You can combine php evalution with the token module, because tokens are replaced first.
|
||||
However be aware to don't use this with any textual values provided by users as this would
|
||||
open a security hole. If you are in doubt, don't combine tokens with php evaluation.
|
||||
|
||||
Here is an example:
|
||||
|
||||
<?php
|
||||
$token = '[field_testnumber]';
|
||||
if (empty($token)) {
|
||||
return '[type]';
|
||||
}
|
||||
else {
|
||||
return $token;
|
||||
}
|
||||
?>
|
||||
|
||||
So if the text of the number field [field_testnumber] isn't empty it will be used as title.
|
||||
Otherwise the node type will be used.
|
||||
|
||||
|
||||
Updating nodetitles from existing nodes
|
||||
---------------------------------------
|
||||
If you set the nodetitle to be auto generated for some content type, existing nodes
|
||||
are not affected. You can update existing nodes by going to 'admin/content',
|
||||
then filter for your content type, mark some nodes and choose the "Update option"
|
||||
"Update automatic nodetitles".
|
||||
|
||||
|
@@ -0,0 +1,13 @@
|
||||
; $Id.
|
||||
name = Automatic Nodetitles
|
||||
description = Allows hiding of the content title field and automatic title creation.
|
||||
core = 7.x
|
||||
files[] = auto_nodetitle.install
|
||||
files[] = auto_nodetitle.module
|
||||
files[] = auto_nodetitle.js
|
||||
; Information added by drupal.org packaging script on 2011-06-07
|
||||
version = "7.x-1.0"
|
||||
core = "7.x"
|
||||
project = "auto_nodetitle"
|
||||
datestamp = "1307449915"
|
||||
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Installation file for the automatic nodetitle module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function auto_nodetitle_install() {
|
||||
db_query("UPDATE {system} SET weight = 5 WHERE name = 'auto_nodetitle'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function auto_nodetitle_uninstall() {
|
||||
foreach (node_type_get_names('names') as $type => $type_name) {
|
||||
variable_del('ant_' . $type);
|
||||
variable_del('ant_pattern_' . $type);
|
||||
variable_del('ant_php_' . $type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure hooks are invoked after cck main hooks
|
||||
*/
|
||||
function auto_nodetitle_update_1() {
|
||||
$ret = array();
|
||||
$ret[] = update_sql("UPDATE {system} SET weight = 5 WHERE name = 'auto_nodetitle'");
|
||||
return $ret;
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
(function ($) {
|
||||
|
||||
Drupal.behaviors.auto_nodetitleFieldsetSummaries = {
|
||||
attach: function (context) {
|
||||
$('fieldset#edit-auto-nodetitle', context).drupalSetSummary(function (context) {
|
||||
|
||||
// Retrieve the value of the selected radio button
|
||||
var ant = $("input[@name=#edit-auto-nodetitle-ant]:checked").val();
|
||||
|
||||
if (ant==0) {
|
||||
return Drupal.t('Disabled')
|
||||
}
|
||||
else if (ant==1) {
|
||||
return Drupal.t('Automatic (hide title field)')
|
||||
}
|
||||
else if (ant==2) {
|
||||
return Drupal.t('Automatic (if title empty)')
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Allows hiding of the node title field and automatic title creation.
|
||||
*/
|
||||
|
||||
define('AUTO_NODETITLE_DISABLED', 0);
|
||||
define('AUTO_NODETITLE_ENABLED', 1);
|
||||
define('AUTO_NODETITLE_OPTIONAL', 2);
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function auto_nodetitle_permission() {
|
||||
return array(
|
||||
'use PHP for title patterns' => array(
|
||||
'title' => t('Use PHP for title patterns'),
|
||||
'description' => t('Use PHP for title patterns.'),
|
||||
'restrict access' => TRUE,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for the node form.
|
||||
*/
|
||||
function auto_nodetitle_form_node_form_alter(&$form, &$form_state, $form_id) {
|
||||
if (auto_nodetitle_get_setting($form['#node']->type) == AUTO_NODETITLE_ENABLED) {
|
||||
// We will autogenerate the title later, just hide the title field in the
|
||||
// meanwhile.
|
||||
$form['title']['#value'] = 'ant';
|
||||
$form['title']['#type'] = 'value';
|
||||
$form['title']['#required'] = FALSE;
|
||||
}
|
||||
elseif (auto_nodetitle_get_setting($form['#node']->type) == AUTO_NODETITLE_OPTIONAL) {
|
||||
$form['title']['#required'] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_submit().
|
||||
*
|
||||
* Generate the node title as soon as the form has been submitted. That way
|
||||
* the node preview is shown right too.
|
||||
*/
|
||||
function auto_nodetitle_node_submit($node, $form, &$form_state) {
|
||||
$setting = auto_nodetitle_get_setting($node->type);
|
||||
if ($setting == AUTO_NODETITLE_ENABLED || ($setting == AUTO_NODETITLE_OPTIONAL && empty($form_state['values']['title']))) {
|
||||
auto_nodetitle_set_title($node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_presave().
|
||||
*/
|
||||
function auto_nodetitle_node_presave($node) {
|
||||
// If not yet done, generate the title now.
|
||||
if (auto_nodetitle_is_needed($node)) {
|
||||
auto_nodetitle_set_title($node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the auto nodetitle has to be set.
|
||||
*/
|
||||
function auto_nodetitle_is_needed($node) {
|
||||
return empty($node->auto_nodetitle_applied) && ($setting = auto_nodetitle_get_setting($node->type)) && !($setting == AUTO_NODETITLE_OPTIONAL && !empty($node->title));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the automatically generated nodetitle for the node
|
||||
*/
|
||||
function auto_nodetitle_set_title(&$node) {
|
||||
$types = node_type_get_types();
|
||||
$pattern = variable_get('ant_pattern_' . $node->type, '');
|
||||
if (trim($pattern)) {
|
||||
$node->changed = REQUEST_TIME;
|
||||
$node->title = _auto_nodetitle_patternprocessor($pattern, $node);
|
||||
}
|
||||
elseif ($node->nid) {
|
||||
$node->title = t('@type @node-id', array('@type' => $types[$node->type]->name, '@node-id' => $node->nid));
|
||||
}
|
||||
else {
|
||||
$node->title = t('@type', array('@type' => $types[$node->type]->name));
|
||||
}
|
||||
// Ensure the generated title isn't too long.
|
||||
$node->title = substr($node->title, 0, 255);
|
||||
// With that flag we ensure we don't apply the title two times to the same
|
||||
// node. See auto_nodetitle_is_needed().
|
||||
$node->auto_nodetitle_applied = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_operations().
|
||||
*/
|
||||
function auto_nodetitle_node_operations() {
|
||||
$operations = array(
|
||||
'nodetitle_update' => array(
|
||||
'label' => t('Update automatic nodetitles'),
|
||||
'callback' => 'auto_nodetitle_operations_update',
|
||||
),
|
||||
);
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for updating node titles.
|
||||
*/
|
||||
function auto_nodetitle_operations_update($nodes) {
|
||||
foreach ($nodes as $nid) {
|
||||
$node = node_load($nid);
|
||||
if ($node && auto_nodetitle_is_needed($node)) {
|
||||
$previous_title = $node->title;
|
||||
auto_nodetitle_set_title($node);
|
||||
// Only save if the title has actually changed.
|
||||
if ($node->title != $previous_title) {
|
||||
node_save($node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to generate the title according to the settings.
|
||||
*
|
||||
* @return a title string
|
||||
*/
|
||||
function _auto_nodetitle_patternprocessor($pattern, $node) {
|
||||
// Replace tokens.
|
||||
$output = token_replace($pattern, array('node' => $node), array('sanitize' => FALSE));
|
||||
// Evalute PHP.
|
||||
if (variable_get('ant_php_' . $node->type, 0)) {
|
||||
$output = auto_nodetitle_eval($output, $node);
|
||||
}
|
||||
// Strip tags.
|
||||
$output = preg_replace('/[\t\n\r\0\x0B]/', '', strip_tags($output));
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for the node type form.
|
||||
*/
|
||||
function auto_nodetitle_form_node_type_form_alter(&$form, &$form_state) {
|
||||
$default_value = auto_nodetitle_get_setting($form['#node_type']->type);
|
||||
$form['auto_nodetitle'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Automatic title generation'),
|
||||
'#weight' => 0,
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => !$default_value,
|
||||
'#group' => 'additional_settings',
|
||||
'#attached' => array(
|
||||
'js' => array(
|
||||
'auto-nodetitle' => drupal_get_path('module', 'auto_nodetitle') . '/auto_nodetitle.js',
|
||||
),
|
||||
),
|
||||
);
|
||||
$form['auto_nodetitle']['ant'] = array(
|
||||
'#type' => 'radios',
|
||||
'#default_value' => $default_value,
|
||||
'#options' => array(
|
||||
t('Disabled'),
|
||||
t('Automatically generate the title and hide the title field'),
|
||||
t('Automatically generate the title if the title field is left empty'),
|
||||
)
|
||||
);
|
||||
$form['auto_nodetitle']['ant_pattern'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Pattern for the title'),
|
||||
'#description' => t('Leave blank for using the per default generated title. Otherwise this string will be used as title. Use the syntax [token] if you want to insert a replacement pattern.'),
|
||||
'#default_value' => variable_get('ant_pattern_' . $form['#node_type']->type, ''),
|
||||
);
|
||||
// Don't allow editing of the pattern if PHP is used, but the users lacks
|
||||
// permission for PHP.
|
||||
if (variable_get('ant_php_' . $form['#node_type']->type, '') && !user_access('use PHP for title patterns')) {
|
||||
$form['auto_nodetitle']['ant_pattern']['#disabled'] = TRUE;
|
||||
$form['auto_nodetitle']['ant_pattern']['#description'] = t('You are not allow the configure the pattern for the title, as you lack the %permission permission.', array('%permission' => t('Use PHP for title patterns')));
|
||||
}
|
||||
|
||||
// Display the list of available placeholders if token module is installed.
|
||||
if (module_exists('token')) {
|
||||
$form['auto_nodetitle']['token_help'] = array(
|
||||
'#theme' => 'token_tree',
|
||||
'#token_types' => array('node'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$form['auto_nodetitle']['ant_php'] = array(
|
||||
'#access' => user_access('use PHP for title patterns'),
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Evaluate PHP in pattern.'),
|
||||
'#description' => t('Put PHP code above that returns your string, but make sure you surround code in <?php and ?>. Note that $node is available and can be used by your code.'),
|
||||
'#default_value' => variable_get('ant_php_' . $form['#node_type']->type, ''),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the auto node title setting associated with the given content type.
|
||||
*/
|
||||
function auto_nodetitle_get_setting($type) {
|
||||
return variable_get('ant_' . $type, AUTO_NODETITLE_DISABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates php code and passes $node to it.
|
||||
*/
|
||||
function auto_nodetitle_eval($code, $node) {
|
||||
ob_start();
|
||||
print eval('?>' . $code);
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_type().
|
||||
*/
|
||||
function auto_nodetitle_node_type($op, $info) {
|
||||
switch ($op) {
|
||||
case 'delete':
|
||||
variable_del('ant_' . $info->type);
|
||||
variable_del('ant_pattern_' . $info->type);
|
||||
variable_del('ant_php_' . $info->type);
|
||||
break;
|
||||
case 'update':
|
||||
if (!empty($info->old_type) && $info->old_type != $info->type) {
|
||||
variable_set('ant_' . $info->type, auto_nodetitle_get_setting($info->old_type));
|
||||
variable_set('ant_pattern_' . $info->type, variable_get('ant_pattern_' . $info->old_type, ''));
|
||||
variable_set('ant_php_' . $info->type, variable_get('ant_php_' . $info->old_type, ''));
|
||||
variable_del('ant_' . $info->old_type);
|
||||
variable_del('ant_pattern_' . $info->old_type);
|
||||
variable_del('ant_php_' . $info->old_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
166
sites/all/modules/contrib/content/diff/CHANGELOG.txt
Normal file
166
sites/all/modules/contrib/content/diff/CHANGELOG.txt
Normal file
@@ -0,0 +1,166 @@
|
||||
|
||||
CHANGELOG for Diff 7.x-2.0+13-dev to 7.x-3.x
|
||||
============================================
|
||||
|
||||
1) System variable names have been changed
|
||||
------------------------------------------
|
||||
|
||||
Considerable changes have occurred.
|
||||
|
||||
2) hook_diff() was removed
|
||||
--------------------------
|
||||
|
||||
This has been replaced by hook_entity_diff() as of Diff 7.x-3.x.
|
||||
|
||||
3) Field diffs are handled independently by Diff and the field module
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Field modules SHOULD NOT implement hook_entity_diff().
|
||||
|
||||
This is complicated and costly in terms of performance.
|
||||
|
||||
Two new field callbacks are defined to handle these.
|
||||
|
||||
a) MODULE_field_diff_view_prepare()
|
||||
|
||||
Optional: If you need to load data, use MODULE_field_diff_view_prepare().
|
||||
|
||||
b) MODULE_field_diff_view()
|
||||
|
||||
Recommended: You should implement this to generate the compared data.
|
||||
|
||||
If there is no corresponding hook for a field, the field comparison will try
|
||||
to guess the value using $item['safe_value'] or $item['value'] properties.
|
||||
|
||||
If you need to make this configurable, there are two additional hooks:
|
||||
|
||||
c) MODULE_field_diff_default_options($field_type)
|
||||
|
||||
You should define any additioal settings here. This shares a global namespace
|
||||
of the diff module, so you can overwrite core Diff settings here too.
|
||||
|
||||
In saying that, take care not to accidentially do this.
|
||||
|
||||
d) MODULE_field_diff_options_form($field_type, $settings)
|
||||
|
||||
This is where you insert Form API elements to configure your option settings.
|
||||
|
||||
4) Field diffs are now configurable
|
||||
-----------------------------------
|
||||
|
||||
Each field type defined by core have configurable settings to control the
|
||||
rendering of the comparison.
|
||||
|
||||
a) Global configuration
|
||||
|
||||
An administration page has been added to handle field type default settings.
|
||||
|
||||
This is the preferred way to configure field settings are these are global to
|
||||
all fields of this type.
|
||||
|
||||
b) View mode display options
|
||||
|
||||
The display "Diff comparison" is used to control the fields that are displayed
|
||||
when comparing different revisions.
|
||||
|
||||
The following is a walk-through on how you would configure the Basic page
|
||||
(page) content types field configuration.
|
||||
|
||||
- Enable "Diff comparison" custom view mode
|
||||
|
||||
Navigate to admin/structure/types/manage/page/display and look at the
|
||||
Custom Display Settings for this view mode. Check and save.
|
||||
|
||||
- Configure the display
|
||||
|
||||
After Saving this page, a new tab appears "Diff comparison", click this or
|
||||
navigate directly to admin/structure/types/manage/page/display/diff_standard
|
||||
|
||||
- You can hide or show the fields that you want to display when doing
|
||||
comparisons.
|
||||
- If the field has no inbuilt diff support, then the renderred field items
|
||||
will be compared.
|
||||
|
||||
5) Standard comparison preview / Inline diff view setting
|
||||
---------------------------------------------------------
|
||||
|
||||
You can set the view modes used to compare the rendered node. This can be found
|
||||
in the Diff settings in the Content Type settings page.
|
||||
|
||||
6) Optional CSS and new Boxes styles
|
||||
------------------------------------
|
||||
|
||||
This takes the styles from WikiPedia to really spice up the diff page.
|
||||
|
||||
7) Optional JScript extras
|
||||
--------------------------
|
||||
|
||||
This spices up the revision checkboxes on the revisions page.
|
||||
|
||||
8) Simple past revision token support
|
||||
-------------------------------------
|
||||
|
||||
Use-case, email notifications when content has changes. If these support tokens,
|
||||
then you can embed Diffs into these emails.
|
||||
|
||||
9) Extensive string review
|
||||
--------------------------
|
||||
See http://drupal.org/node/1785742
|
||||
|
||||
|
||||
10) Inline block settings changes
|
||||
---------------------------------
|
||||
The inline block settings are now in the block configuration page.
|
||||
|
||||
11) And much more...
|
||||
--------------------
|
||||
|
||||
The complete change log follows:
|
||||
|
||||
Diff 7.x-2.x
|
||||
o #888680 by Deciphered, Alan D.: Allow modules to interact via drupal_alter()
|
||||
o #1280892 by Alan D., crea: Diff should track the variables that it defines
|
||||
o #1304658 by Alan D., kari.kaariainen: Remove links and comments from the comparison preview
|
||||
o #1122206 by binford2k, Alan D.: Notices thrown by DiffEngine::process_chunk()
|
||||
o #1175064 by zilverdistel, Alan D.: Provide variables for leading and trailing context
|
||||
o #1673864 by Alan D.: Allow users to bypass the admin theme when viewing comparisons
|
||||
o #1673876 by Alan D.: Use Drupal autoloading for classes
|
||||
o #1673856 by Alan D.: Use hook_form_BASE_FORM_ID_alter() rather than hook_form_alter()
|
||||
o #1673856 by Alan D.: Normalise line endings
|
||||
o #114308 by Alan D.: add jQuery for hiding radios that shouldn't show diffs
|
||||
o #1688840 by Alan D.: Enable new JScript behaviour by default
|
||||
o #372957 by erykmynn, JuliaKM, lsrzj, andrew_rs, alexpott, et al: HTML Strip for Diff, WYSIWYG Friendly
|
||||
(This was refactored in the 7.x-3.x branch from the commited 7.x-2.x code)
|
||||
o #521212 by Alan D., blakehall: Make diff comparison page themable
|
||||
o #1671484 by Alan D.: Show number of lines changed on revisions page
|
||||
o #114699 by smokris, Alan D.: Diff module should support Token
|
||||
o #372957 by c31ck: display either Hide or Show based on what clicking it will do at any time (HTML Strip for Diff)
|
||||
This was altered for the 7.x-3.x branch.
|
||||
o #1807510 & #1825202: Simplify Diff administration
|
||||
o #1812162 by mitchell, Alan D.: 'Highlight changes' block appears on edit form
|
||||
|
||||
Node to Entity changes
|
||||
----------------------
|
||||
These are roughly tracked in the meta issue #1365750 Generalize API and Integrate with core field types
|
||||
|
||||
o (no issue) by Alan D.: Use entity specific system variables.
|
||||
o (no issue) by Alan D.: View mode code, new hooks, new API. Massive patch!
|
||||
|
||||
Resolves:
|
||||
o #248778: Taxonomy diff
|
||||
o #1550698: Diff of "select from list" fields shows change in key, not change in value
|
||||
o #1458814: File (and image) field support
|
||||
o #1418760: Optional setting to honour the display settings
|
||||
o #1347316: Selectable view mode for inline diffs and "Current revision" display view mode
|
||||
o #1458906: Improve performances (of existing 7.x-2.x field rendering)
|
||||
o #1424162: Diff in Taxonomy term description
|
||||
o #1211282: Image diff support
|
||||
|
||||
The following patches will be posted in the corresponding project queues once
|
||||
the 7.x-3.x branch is released:
|
||||
o #1595702 by Alan D., mbilbille: Support of field collection module
|
||||
o #1350604 by Alan D., johaziel: Datetime diff
|
||||
o (no issue) by Alan D.: Email field Diff support
|
||||
o (no issue) by Alan D.: Countries Diff support
|
||||
o (no issue) by Alan D.: Name field Diff support
|
||||
o (no issue) by Alan D.: Link field Diff support
|
1349
sites/all/modules/contrib/content/diff/DiffEngine.php
Normal file
1349
sites/all/modules/contrib/content/diff/DiffEngine.php
Normal file
File diff suppressed because it is too large
Load Diff
339
sites/all/modules/contrib/content/diff/LICENSE.txt
Normal file
339
sites/all/modules/contrib/content/diff/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.
|
124
sites/all/modules/contrib/content/diff/css/diff.boxes.css
Normal file
124
sites/all/modules/contrib/content/diff/css/diff.boxes.css
Normal file
@@ -0,0 +1,124 @@
|
||||
|
||||
html.js .diff-js-hidden { display: none; }
|
||||
|
||||
/* Reset as many core themes as possible */
|
||||
table.diff {
|
||||
font-size: 0.923em;
|
||||
margin: 0 0 10px;
|
||||
border: 0 none;
|
||||
width: 98%;
|
||||
border-spacing: 5px;
|
||||
table-layout: fixed ;
|
||||
border-collapse: separate;
|
||||
}
|
||||
table.diff tr td:last-child {
|
||||
border-right: inherit;
|
||||
}
|
||||
table.diff td,
|
||||
table.diff th {
|
||||
vertical-align: middle;
|
||||
border: 0 none;
|
||||
color: #000;
|
||||
text-transform: none;
|
||||
background: none;
|
||||
border-spacing: 4px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
table.diff tr, table.diff tr.even {
|
||||
background: none;
|
||||
}
|
||||
table.diff tr th, table.diff tr th a, table.diff tr th a:hover {
|
||||
color: inherit;
|
||||
font-weight: bold;
|
||||
}
|
||||
table.diff tr.even,
|
||||
table.diff tr.odd {
|
||||
border-width: 0;
|
||||
border-style: none;
|
||||
background: transparent;
|
||||
}
|
||||
table.diff th a { display: inline; }
|
||||
|
||||
/* Main theming */
|
||||
table.diff, td.diff-number {
|
||||
background-color: white
|
||||
}
|
||||
|
||||
table.diff td.diff-lineno {
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
table.diff td.diff-addedline, table.diff td.diff-deletedline, table.diff td.diff-context {
|
||||
font-size: 88%;
|
||||
vertical-align: top;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: pre-wrap
|
||||
}
|
||||
|
||||
table.diff td.diff-addedline, table.diff td.diff-deletedline {
|
||||
border-style: solid;
|
||||
border-width: 1px 1px 1px 4px;
|
||||
border-radius: 0.33em
|
||||
}
|
||||
|
||||
table.diff td.diff-context {
|
||||
background: #f3f3f3;
|
||||
color: #333333;
|
||||
border-style: solid;
|
||||
border-width: 1px 1px 1px 4px;
|
||||
border-color: #e6e6e6;
|
||||
border-radius: 0.33em;
|
||||
}
|
||||
table.diff td.diff-addedline {
|
||||
border-color: #a3d3ff;
|
||||
background: #ffffff;
|
||||
border: 1px 1px 1px 3px;
|
||||
}
|
||||
|
||||
table.diff td.diff-deletedline {
|
||||
border-color: #ffe49c
|
||||
}
|
||||
|
||||
.diffchange {
|
||||
font-weight: bold;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
table.diff td.diff-addedline .diffchange, table.diff td.diff-deletedline .diffchange {
|
||||
border-radius: 0.33em;
|
||||
padding: 0.25em 0
|
||||
}
|
||||
|
||||
table.diff td.diff-addedline .diffchange {
|
||||
background: #d8ecff
|
||||
}
|
||||
|
||||
table.diff td.diff-deletedline .diffchange {
|
||||
background: #feeec8
|
||||
}
|
||||
|
||||
table.diff table.diff td {
|
||||
padding: 0.33em 0.66em
|
||||
}
|
||||
|
||||
table.diff td.diff-marker {
|
||||
width: 2%;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
font-size: 1.25em
|
||||
}
|
||||
|
||||
table.diff col.diff-content {
|
||||
width: 48%
|
||||
}
|
||||
|
||||
table.diff table.diff td div {
|
||||
word-wrap: break-word;
|
||||
overflow: auto
|
||||
}
|
||||
td.diff-prevlink {
|
||||
text-align: left;
|
||||
}
|
||||
td.diff-nextlink {
|
||||
text-align: right;
|
||||
}
|
86
sites/all/modules/contrib/content/diff/css/diff.default.css
Normal file
86
sites/all/modules/contrib/content/diff/css/diff.default.css
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
html.js .diff-js-hidden { display: none; }
|
||||
|
||||
/**
|
||||
* Inline diff metadata
|
||||
*/
|
||||
.diff-inline-metadata {
|
||||
padding:4px;
|
||||
border:1px solid #ddd;
|
||||
background:#fff;
|
||||
margin:0px 0px 10px;
|
||||
}
|
||||
|
||||
.diff-inline-legend { font-size:11px; }
|
||||
|
||||
.diff-inline-legend span,
|
||||
.diff-inline-legend label { margin-right:5px; }
|
||||
|
||||
/**
|
||||
* Inline diff markup
|
||||
*/
|
||||
span.diff-deleted { color:#ccc; }
|
||||
span.diff-deleted img { border: solid 2px #ccc; }
|
||||
span.diff-changed { background:#ffb; }
|
||||
span.diff-changed img { border:solid 2px #ffb; }
|
||||
span.diff-added { background:#cfc; }
|
||||
span.diff-added img { border: solid 2px #cfc; }
|
||||
|
||||
/**
|
||||
* Traditional split diff theming
|
||||
*/
|
||||
table.diff {
|
||||
border-spacing: 4px;
|
||||
margin-bottom: 20px;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
table.diff tr.even, table.diff tr.odd {
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
}
|
||||
td.diff-prevlink {
|
||||
text-align: left;
|
||||
}
|
||||
td.diff-nextlink {
|
||||
text-align: right;
|
||||
}
|
||||
td.diff-section-title, div.diff-section-title {
|
||||
background-color: #f0f0ff;
|
||||
font-size: 0.83em;
|
||||
font-weight: bold;
|
||||
padding: 0.1em 1em;
|
||||
}
|
||||
td.diff-context {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
td.diff-deletedline {
|
||||
background-color: #ffa;
|
||||
width: 50%;
|
||||
}
|
||||
td.diff-addedline {
|
||||
background-color: #afa;
|
||||
width: 50%;
|
||||
}
|
||||
span.diffchange {
|
||||
color: #f00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.diff col.diff-marker {
|
||||
width: 1.4em;
|
||||
}
|
||||
table.diff col.diff-content {
|
||||
width: 50%;
|
||||
}
|
||||
table.diff th {
|
||||
padding-right: inherit;
|
||||
}
|
||||
table.diff td div {
|
||||
overflow: auto;
|
||||
padding: 0.1ex 0.5em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
table.diff td {
|
||||
padding: 0.1ex 0.4em;
|
||||
}
|
158
sites/all/modules/contrib/content/diff/diff.admin.inc
Normal file
158
sites/all/modules/contrib/content/diff/diff.admin.inc
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Administration page callbacks and forms.
|
||||
*/
|
||||
|
||||
/**
|
||||
* General configuration form for controlling the diff behaviour.
|
||||
*/
|
||||
function diff_admin_settings($form, $form_state) {
|
||||
$form['diff_theme'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('CSS options'),
|
||||
'#default_value' => variable_get('diff_theme', 'default'),
|
||||
'#options' => array(
|
||||
'default' => t('Classic'),
|
||||
'boxes' => t('Boxes'),
|
||||
),
|
||||
'#empty_option' => t('- None -'),
|
||||
'#description' => t('Alter the CSS used when displaying diff results.'),
|
||||
);
|
||||
$form['diff_default_state_node'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Diff default state'),
|
||||
'#default_value' => variable_get('diff_default_state_node', 'raw'),
|
||||
'#options' => array(
|
||||
'raw' => t('HTML view'),
|
||||
'raw_plain' => t('Plain view'),
|
||||
),
|
||||
'#empty_option' => t('- None -'),
|
||||
'#description' => t('Default display to show when viewing a diff, html tags in diffed result or as plain text.'),
|
||||
);
|
||||
$form['diff_radio_behavior'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Diff radio behavior'),
|
||||
'#default_value' => variable_get('diff_radio_behavior', 'simple'),
|
||||
'#options' => array(
|
||||
'simple' => t('Simple exclusion'),
|
||||
'linear' => t('Linear restrictions'),
|
||||
),
|
||||
'#empty_option' => t('- None -'),
|
||||
'#description' => t('<em>Simple exclusion</em> means that users will not be able to select the same revision, <em>Linear restrictions</em> means that users can only select older or newer revisions of the current selections.'),
|
||||
);
|
||||
|
||||
$options = drupal_map_assoc(array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
|
||||
$form['diff_context_lines_leading'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Leading context lines'),
|
||||
'#description' => t('This governs the number of unchanged leading context "lines" to preserve.'),
|
||||
'#default_value' => variable_get('diff_context_lines_leading', 2),
|
||||
'#options' => $options,
|
||||
);
|
||||
$form['diff_context_lines_trailing'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Trailing context lines'),
|
||||
'#description' => t('This governs the number of unchanged trailing context "lines" to preserve.'),
|
||||
'#default_value' => variable_get('diff_context_lines_trailing', 2),
|
||||
'#options' => $options,
|
||||
);
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Global entity settings.
|
||||
*/
|
||||
function diff_admin_global_entity_settings($form, $form_state, $entity_type) {
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
drupal_set_title(t('Diff settings for %entity_label entities', array('%entity_label' => $entity_info['label'])), PASS_THROUGH);
|
||||
$form['diff_show_header_' . $entity_type] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show entity label header'),
|
||||
'#default_value' => variable_get('diff_show_header_' . $entity_type, 1),
|
||||
);
|
||||
$form['diff_admin_path_' . $entity_type] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Treat diff pages as administrative'),
|
||||
'#description' => t('Diff pages are treated as administrative pages by default, although it is up to each module to enforce this and to implement this optional setting.'),
|
||||
'#default_value' => variable_get('diff_admin_path_' . $entity_type, 1),
|
||||
);
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback - provides an overview of Diff support and global settings.
|
||||
*/
|
||||
function diff_admin_field_overview() {
|
||||
$build['info'] = array(
|
||||
'#markup' => '<p>' . t('This table provides a summary of the field type support found on the system. It is recommended that you use global settings whenever possible to configure field comparison settings.') . '</p>',
|
||||
);
|
||||
|
||||
$header = array(t('Type'), t('Module'), t('Operations'));
|
||||
$rows = array();
|
||||
|
||||
// Skip field types which have no widget types.
|
||||
$field_types = field_info_field_types();
|
||||
$widgets = array();
|
||||
foreach (field_info_widget_types() as $name => $widget_type) {
|
||||
foreach ($widget_type['field types'] as $widget_field_type) {
|
||||
if (isset($field_types[$widget_field_type])) {
|
||||
$widgets[$widget_field_type][$name] = $widget_type['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($field_types as $field_name => $field_type) {
|
||||
if (!empty($widgets[$field_name])) {
|
||||
$row = array();
|
||||
$row[] = t('@field_label (%field_type)', array(
|
||||
'@field_label' => $field_type['label'],
|
||||
'%field_type' => $field_name,
|
||||
));
|
||||
$row[] = $field_type['module'];
|
||||
$row[] = l(t('Global settings'), 'admin/config/content/diff/fields/' . $field_name);
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$build['category_table'] = array(
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#empty' => t('The system has no configurable fields.'),
|
||||
);
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu form callback for the field settings.
|
||||
*/
|
||||
function diff_admin_global_field_settings($form, $form_state, $type) {
|
||||
module_load_include('diff.inc', 'diff');
|
||||
|
||||
$field_types = field_info_field_types();
|
||||
if (!isset($field_types[$type])) {
|
||||
drupal_set_message(t('Invalid field type.'), 'error');
|
||||
drupal_goto('admin/config/content/diff/fields');
|
||||
}
|
||||
$field_type = $field_types[$type];
|
||||
|
||||
// Set the title to give more context to this page.
|
||||
drupal_set_title(t('Global settings for %label fields', array(
|
||||
'%label' => $field_type['label'],
|
||||
)), PASS_THROUGH);
|
||||
|
||||
$variable_name = "diff_{$field_type['module']}_field_{$type}_default_options";
|
||||
$settings = variable_get($variable_name, array());
|
||||
$settings = _diff_field_default_settings($field_type['module'], $type, $settings);
|
||||
$func = $field_type['module'] . '_field_diff_options_form';
|
||||
if (function_exists($func) && ($options_form = $func($type, $settings))) {
|
||||
$form[$variable_name] = $options_form;
|
||||
}
|
||||
$form[$variable_name]['#tree'] = TRUE;
|
||||
|
||||
diff_global_settings_form($form[$variable_name], $form_state, $type, $settings);
|
||||
return system_settings_form($form);
|
||||
}
|
181
sites/all/modules/contrib/content/diff/diff.api.php
Normal file
181
sites/all/modules/contrib/content/diff/diff.api.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the diff module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allow modules to provide a comparison about entities.
|
||||
*
|
||||
* @param object $old_entity
|
||||
* The older entity revision.
|
||||
* @param object $new_entity
|
||||
* The newer entity revision.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - view_mode: The view mode to use. Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of values keyed by the entity property.
|
||||
*
|
||||
* @todo
|
||||
* Investiagate options and document these.
|
||||
*/
|
||||
function hook_entity_diff($old_entity, $new_entity, $context) {
|
||||
if ($context['entity_type'] == 'node') {
|
||||
$type = node_type_get_type($new_entity);
|
||||
$result['title'] = array(
|
||||
'#name' => $type->title_label,
|
||||
'#old' => array($old_entity->title),
|
||||
'#new' => array($new_entity->title),
|
||||
'#weight' => -5,
|
||||
'#settings' => array(
|
||||
'show_header' => FALSE,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow modules to alter a comparison about entities.
|
||||
*
|
||||
* @param array $entity_diffs
|
||||
* An array of entity differences.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
* - view_mode: The view mode to use. Defaults to FALSE.
|
||||
*
|
||||
* @see hook_entity_diff()
|
||||
*/
|
||||
function hook_entity_diff_alter($entity_diffs, $context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to the module that defined the field to prepare items comparison.
|
||||
*
|
||||
* This allows the module to alter all items prior to rendering the comparative
|
||||
* values. It is mainly used to bulk load entities to reduce overheads
|
||||
* associated with loading entities individually.
|
||||
*
|
||||
* @param array $old_items
|
||||
* An array of field items from the older revision.
|
||||
* @param array $new_items
|
||||
* An array of field items from the newer revision.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - bundle: The bundle name.
|
||||
* - field: The field that the items belong to.
|
||||
* - instance: The instance that the items belong to.
|
||||
* - language: The language associated with $items.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
*
|
||||
* @see MODULE_field_diff_view()
|
||||
*/
|
||||
function MODULE_field_diff_view_prepare(&$old_items, &$new_items, $context) {
|
||||
$fids = array();
|
||||
foreach (array_merge_recursive($old_items, $new_items) as $info) {
|
||||
$fids[$info['fid']] = $info['fid'];
|
||||
}
|
||||
// A single load is much faster than individual loads.
|
||||
$files = file_load_multiple($fids);
|
||||
|
||||
// For ease of processing, store a reference of the entity on the item array.
|
||||
foreach ($old_items as $delta => $info) {
|
||||
$old_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
|
||||
}
|
||||
foreach ($new_items as $delta => $info) {
|
||||
$new_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to the module that defined the field to generate items comparisons.
|
||||
*
|
||||
* @param array $items
|
||||
* An array of field items from the entity.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity: The entity being compared.
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - bundle: The bundle name.
|
||||
* - field: The field that the items belong to.
|
||||
* - instance: The instance that the items belong to.
|
||||
* - language: The language associated with $items.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
*
|
||||
* @see MODULE_field_diff_view_prepare()
|
||||
*/
|
||||
function MODULE_field_diff_view($items, $context) {
|
||||
$diff_items = array();
|
||||
foreach ($items as $delta => $item) {
|
||||
if (isset($item['file'])) {
|
||||
$diff_items[$delta] = $item['file']->filename . ' [fid: ' . $item['fid'] . ']';
|
||||
}
|
||||
}
|
||||
|
||||
return $diff_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow other modules to interact with MODULE_field_diff_view_prepare().
|
||||
*
|
||||
* @param array $old_items
|
||||
* An array of field items from the older revision.
|
||||
* @param array $new_items
|
||||
* An array of field items from the newer revision.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - bundle: The bundle name.
|
||||
* - field: The field that the items belong to.
|
||||
* - instance: The instance that the items belong to.
|
||||
* - language: The language associated with $items.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
*
|
||||
* @see MODULE_field_diff_view_prepare()
|
||||
*/
|
||||
function hook_field_diff_view_prepare_alter($old_items, $new_items, $context) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow other modules to interact with MODULE_field_diff_view().
|
||||
*
|
||||
* @param array $values
|
||||
* An array of field items from the entity ready for comparison.
|
||||
* @param array $items
|
||||
* An array of field items from the entity.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity: The entity being compared.
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - bundle: The bundle name.
|
||||
* - field: The field that the items belong to.
|
||||
* - instance: The instance that the items belong to.
|
||||
* - language: The language associated with $items.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
*
|
||||
* @see MODULE_field_diff_view()
|
||||
*/
|
||||
function hook_field_diff_view_alter($values, $items, $context) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
86
sites/all/modules/contrib/content/diff/diff.css
Normal file
86
sites/all/modules/contrib/content/diff/diff.css
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
html.js .diff-js-hidden { display:none; }
|
||||
|
||||
/**
|
||||
* Inline diff metadata
|
||||
*/
|
||||
.diff-inline-metadata {
|
||||
padding:4px;
|
||||
border:1px solid #ddd;
|
||||
background:#fff;
|
||||
margin:0px 0px 10px;
|
||||
}
|
||||
|
||||
.diff-inline-legend { font-size:11px; }
|
||||
|
||||
.diff-inline-legend span,
|
||||
.diff-inline-legend label { margin-right:5px; }
|
||||
|
||||
/**
|
||||
* Inline diff markup
|
||||
*/
|
||||
span.diff-deleted { color:#ccc; }
|
||||
span.diff-deleted img { border: solid 2px #ccc; }
|
||||
span.diff-changed { background:#ffb; }
|
||||
span.diff-changed img { border:solid 2px #ffb; }
|
||||
span.diff-added { background:#cfc; }
|
||||
span.diff-added img { border: solid 2px #cfc; }
|
||||
|
||||
/**
|
||||
* Traditional split diff theming
|
||||
*/
|
||||
table.diff {
|
||||
border-spacing: 4px;
|
||||
margin-bottom: 20px;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
table.diff tr.even, table.diff tr.odd {
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
}
|
||||
td.diff-prevlink {
|
||||
text-align: left;
|
||||
}
|
||||
td.diff-nextlink {
|
||||
text-align: right;
|
||||
}
|
||||
td.diff-section-title, div.diff-section-title {
|
||||
background-color: #f0f0ff;
|
||||
font-size: 0.83em;
|
||||
font-weight: bold;
|
||||
padding: 0.1em 1em;
|
||||
}
|
||||
td.diff-deletedline {
|
||||
background-color: #ffa;
|
||||
width: 50%;
|
||||
}
|
||||
td.diff-addedline {
|
||||
background-color: #afa;
|
||||
width: 50%;
|
||||
}
|
||||
td.diff-context {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
span.diffchange {
|
||||
color: #f00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.diff col.diff-marker {
|
||||
width: 1.4em;
|
||||
}
|
||||
table.diff col.diff-content {
|
||||
width: 50%;
|
||||
}
|
||||
table.diff th {
|
||||
padding-right: inherit;
|
||||
}
|
||||
table.diff td div {
|
||||
overflow: auto;
|
||||
padding: 0.1ex 0.5em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
table.diff td {
|
||||
padding: 0.1ex 0.4em;
|
||||
}
|
384
sites/all/modules/contrib/content/diff/diff.diff.inc
Normal file
384
sites/all/modules/contrib/content/diff/diff.diff.inc
Normal file
@@ -0,0 +1,384 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Includes the hooks defined by diff_hook_info().
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_diff().
|
||||
*
|
||||
* Helper function to invoke the depreciated hook_diff() for node entities.
|
||||
*
|
||||
* This manually invokes hook_diff() to avoid a function name clash with the
|
||||
* PHP 5 (>= 5.3.0) date_diff() function or the Dates modules implementation.
|
||||
*
|
||||
* @param object $old_entity
|
||||
* The older node revision.
|
||||
* @param object $new_entity
|
||||
* The newer node revision.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
* - view_mode: The view mode to use. Defaults to FALSE.
|
||||
*/
|
||||
function diff_entity_diff($old_entity, $new_entity, $context) {
|
||||
$return = array();
|
||||
|
||||
$entity_type = $context['entity_type'];
|
||||
$info = entity_get_info($entity_type);
|
||||
if (!empty($info['fieldable'])) {
|
||||
$return = diff_entity_fields_diff($old_entity, $new_entity, $context);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal callback to handle fieldable entities.
|
||||
*
|
||||
* Field comparison is handled for core modules, but is expandable to any other
|
||||
* fields if the module defines MODULE_field_diff_view().
|
||||
*
|
||||
* @param object $old_entity
|
||||
* The older entity entity revision.
|
||||
* @param object $new_entity
|
||||
* The newer entity entity revision.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
* - view_mode: The view mode to use. Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of values keyed by the field name and delta value.
|
||||
*/
|
||||
function diff_entity_fields_diff($old_entity, $new_entity, $context) {
|
||||
$result = array();
|
||||
|
||||
$entity_type = $context['entity_type'];
|
||||
$view_mode = $context['view_mode'];
|
||||
|
||||
$field_context = $context;
|
||||
|
||||
$actual_mode = FALSE;
|
||||
list(,, $bundle_name) = entity_extract_ids($entity_type, $new_entity);
|
||||
$instances = field_info_instances($entity_type, $bundle_name);
|
||||
|
||||
// Some fields piggy back the display settings, so we need to fake these by
|
||||
// ensuring that the field mode is always set.
|
||||
if (empty($view_mode)) {
|
||||
$actual_mode = 'diff_standard';
|
||||
$field_context['custom_settings'] = FALSE;
|
||||
}
|
||||
$view_mode_settings = field_view_mode_settings($entity_type, $bundle_name);
|
||||
$actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings'])) ? $view_mode : 'default';
|
||||
if (!isset($field_context['custom_settings'])) {
|
||||
$field_context['custom_settings'] = $actual_mode && $actual_mode == $view_mode;
|
||||
}
|
||||
|
||||
$field_context['old_entity'] = $old_entity;
|
||||
$field_context['new_entity'] = $new_entity;
|
||||
$field_context['bundle_name'] = $bundle_name;
|
||||
|
||||
foreach ($instances as $instance) {
|
||||
// Any view mode is supported in relation to hiding fields, but only if
|
||||
// selected (todo see if this is a valid option).
|
||||
if ($actual_mode && $instance['display'][$actual_mode]['type'] == 'hidden') {
|
||||
continue;
|
||||
}
|
||||
$field_name = $instance['field_name'];
|
||||
$field = field_info_field($field_name);
|
||||
$field_context['field'] = $field;
|
||||
$field_context['instance'] = $instance;
|
||||
$field_context['display'] = $instance['display'][$actual_mode];
|
||||
|
||||
// We provide a loose check on the field access.
|
||||
if (field_access('view', $field, $entity_type) || field_access('edit', $field, $entity_type)) {
|
||||
$langcode = field_language($entity_type, $new_entity, $field_name);
|
||||
|
||||
$field_context['language'] = $langcode;
|
||||
$field_context['field'] = $field;
|
||||
$field_context['instance'] = $instance;
|
||||
|
||||
$old_items = array();
|
||||
if (!empty($old_entity->{$field_name}[$langcode])) {
|
||||
$old_items = $old_entity->{$field_name}[$langcode];
|
||||
}
|
||||
|
||||
$new_items = array();
|
||||
if (!empty($new_entity->{$field_name}[$langcode])) {
|
||||
$new_items = $new_entity->{$field_name}[$langcode];
|
||||
}
|
||||
|
||||
// Load files containing the field callbacks.
|
||||
_diff_autoload($field);
|
||||
|
||||
$field_context['settings'] = diff_get_field_settings($field_context);
|
||||
|
||||
// Reference fields can optionally prepare objects in bulk to reduce
|
||||
// overheads related to multiple database calls. If a field considers
|
||||
// that the delta values is meaningless, they can order and rearrange
|
||||
// to provide cleaner results.
|
||||
$func = $field['module'] . '_field_diff_view_prepare';
|
||||
if (function_exists($func)) {
|
||||
$func($old_items, $new_items, $field_context);
|
||||
}
|
||||
// Allow other modules to act safely on behalf of the core field module.
|
||||
drupal_alter('field_diff_view_prepare', $old_items, $new_items, $field_context);
|
||||
|
||||
// These functions compiles the items into comparable arrays of strings.
|
||||
$func = $field['module'] . '_field_diff_view';
|
||||
if (!function_exists($func)) {
|
||||
$func = 'diff_field_diff_view';
|
||||
}
|
||||
|
||||
// These callbacks should be independent of revision.
|
||||
$old_context = $field_context;
|
||||
$old_context['entity'] = $old_entity;
|
||||
$old_values = $func($old_items, $old_context);
|
||||
$new_context = $field_context;
|
||||
$new_context['entity'] = $new_entity;
|
||||
$new_values = $func($new_items, $new_context);
|
||||
|
||||
// Allow other modules to act safely on behalf of the core field module.
|
||||
drupal_alter('field_diff_view', $old_values, $old_items, $old_context);
|
||||
drupal_alter('field_diff_view', $new_values, $new_items, $new_context);
|
||||
|
||||
$max = max(array(count($old_values), count($new_values)));
|
||||
if ($max) {
|
||||
$result[$field_name] = array(
|
||||
'#name' => $instance['label'],
|
||||
'#old' => array(),
|
||||
'#new' => array(),
|
||||
'#settings' => $field_context['settings'],
|
||||
);
|
||||
for ($delta = 0; $delta < $max; $delta++) {
|
||||
if (isset($old_values[$delta])) {
|
||||
$result[$field_name]['#old'][] = is_array($old_values[$delta]) ? implode("\n", $old_values[$delta]) : $old_values[$delta];
|
||||
}
|
||||
if (isset($new_values[$delta])) {
|
||||
$result[$field_name]['#new'][] = is_array($new_values[$delta]) ? implode("\n", $new_values[$delta]) : $new_values[$delta];
|
||||
}
|
||||
}
|
||||
$result[$field_name]['#old'] = implode("\n", $result[$field_name]['#old']);
|
||||
$result[$field_name]['#new'] = implode("\n", $result[$field_name]['#new']);
|
||||
|
||||
if ($actual_mode) {
|
||||
$result[$field_name]['#weight'] = $instance['display'][$actual_mode]['weight'];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic handler for parsing field values.
|
||||
*
|
||||
* This callback can only handle the most basic of fields that populates the
|
||||
* safe_value during field load or use the value column for data storage.
|
||||
*
|
||||
* @param array $items
|
||||
* An array of field items.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity: The entity that the items belong to.
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - bundle: The bundle name.
|
||||
* - field: The field that the items belong to.
|
||||
* - instance: The instance that the items belong to.
|
||||
* - language: The language associated with $items.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
*
|
||||
* @return array
|
||||
* An array of strings representing the value, keyed by delta index.
|
||||
*/
|
||||
function diff_field_diff_view($items, $context) {
|
||||
$diff_items = array();
|
||||
$entity = clone $context['entity'];
|
||||
$langcode = field_language($context['entity_type'], $entity, $context['field']['field_name']);
|
||||
$view_mode = empty($context['view_mode']) ? 'diff_standard' : $context['view_mode'];
|
||||
$element = field_view_field($context['entity_type'], $entity, $context['field']['field_name'], $view_mode, $langcode);
|
||||
|
||||
foreach (element_children($element) as $delta) {
|
||||
$diff_items[$delta] = drupal_render($element[$delta]);
|
||||
}
|
||||
return $diff_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the settings for a given field or formatter.
|
||||
*
|
||||
* @param array $context
|
||||
* This will get the settings for a field.
|
||||
* - field (required): The field that the items belong to.
|
||||
* - entity: The entity that we are looking up.
|
||||
* - instance: The instance that the items belong to.
|
||||
* - view_mode: The view mode to use. Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* The settings for this field type.
|
||||
*/
|
||||
function diff_get_field_settings($field_context) {
|
||||
$field = $field_context['field'];
|
||||
|
||||
// Update saved settings from the global settings for this field type.
|
||||
$settings = variable_get("diff_{$field['module']}_field_{$field['type']}_default_options", array());
|
||||
|
||||
$settings = _diff_field_default_settings($field['module'], $field['type'], $settings);
|
||||
|
||||
// Allow modules to alter the field settings based on the current context.
|
||||
drupal_alter('diff_field_settings', $settings, $field_context);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to initiate any global form elements.
|
||||
*/
|
||||
function diff_global_settings_form(&$subform, $form_state, $type, $settings) {
|
||||
$subform['show_header'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show field title'),
|
||||
'#default_value' => $settings['show_header'],
|
||||
'#weight' => -5,
|
||||
);
|
||||
$subform['markdown'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Markdown callback'),
|
||||
'#default_value' => $settings['markdown'],
|
||||
'#options' => array(
|
||||
'drupal_html_to_text' => t('Drupal HTML to Text'),
|
||||
'filter_xss' => t('Filter XSS (some tags)'),
|
||||
'diff_filter_xss' => t('Filter XSS (all tags)'),
|
||||
),
|
||||
'#description' => t('These provide ways to clean markup tags to make comparisons easier to read.'),
|
||||
'#empty_option' => t('- Do not process -'),
|
||||
);
|
||||
$subform['line_counter'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Line counter'),
|
||||
'#default_value' => $settings['line_counter'],
|
||||
'#description' => t('This outputs the (approximate) line numbers as a heading before every change.'),
|
||||
'#options' => array(
|
||||
'' => t('None. Counter ignore and not incremented.'),
|
||||
'hidden' => t('Count lines but do not show line headers.'),
|
||||
'line' => t('Count and show lines, restarting counter at 0.'),
|
||||
'line_continuous' => t('Count and show lines, incrementing counter from last item.'),
|
||||
),
|
||||
);
|
||||
|
||||
/*
|
||||
This would be cool, but to do anything else than inline with the text appears
|
||||
to be very hard, requiring a refactoring of both the modules API but also the
|
||||
DiffFormatter and Diff classes. Diff 8.x-4.x maybe.
|
||||
|
||||
$subform['show_delta'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show delta values'),
|
||||
'#default_value' => $settings['show_delta'],
|
||||
);
|
||||
$subform['delta_format'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Delta insertion method'),
|
||||
'#default_value' => $settings['delta_format'],
|
||||
'#options' => array(
|
||||
'inline' => t('Prefix to item'),
|
||||
'row' => t('Individual row'),
|
||||
),
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
"input[id$='show-delta']" => array('checked' => FALSE),
|
||||
),
|
||||
),
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to populate the settings array.
|
||||
*/
|
||||
function _diff_field_default_settings($module, $field_type, $settings = array()) {
|
||||
// Load files containing the field callbacks.
|
||||
_diff_autoload($module);
|
||||
|
||||
// Populate any missing values from CALLBACK_field_diff_default_options().
|
||||
$func = $module . '_field_diff_default_options';
|
||||
if (function_exists($func)) {
|
||||
$settings += $func($field_type);
|
||||
}
|
||||
|
||||
// Check for Diff support. If it doesn't exist, the default markdown should
|
||||
// escape the field display, otherwise a raw format should be used.
|
||||
$func = $module . '_field_diff_view';
|
||||
|
||||
// Global settings.
|
||||
$settings += array(
|
||||
'markdown' => function_exists($func) ? '' : 'drupal_html_to_text',
|
||||
'line_counter' => '',
|
||||
'show_header' => 1,
|
||||
// Can we? This seems too hard to track in the DiffFormatter as all we
|
||||
// have is a string or an array of strings.
|
||||
//'show_delta' => 0,
|
||||
//'delta_format' => 'row',
|
||||
);
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper function to load field includes.
|
||||
*
|
||||
* @param array|string $field_or_module
|
||||
* The field definition array or the module that implements the field.
|
||||
*/
|
||||
function _diff_autoload($field_or_module) {
|
||||
$includes = &drupal_static(__FUNCTION__, FALSE);
|
||||
if (!$includes) {
|
||||
$includes = array(
|
||||
'file' => module_exists('file'),
|
||||
'image' => module_exists('image'),
|
||||
'list' => module_exists('list'),
|
||||
'taxonomy' => module_exists('taxonomy'),
|
||||
'text' => module_exists('text'),
|
||||
'number' => module_exists('number'),
|
||||
);
|
||||
}
|
||||
|
||||
$module = is_string($field_or_module) ? $field_or_module : $field_or_module['module'];
|
||||
|
||||
// Since field hooks are not real hooks, we manually load the field modules
|
||||
// MODULE.diff.inc. We handle the five core field defining modules.
|
||||
if (!isset($includes[$module])) {
|
||||
module_load_include('diff.inc', $module);
|
||||
$includes[$module] = 0;
|
||||
}
|
||||
elseif (!empty($includes[$module])) {
|
||||
module_load_include('inc', 'diff', 'includes/' . $module);
|
||||
$includes[$module] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to parse out the state in the diff results.
|
||||
*/
|
||||
function diff_extract_state($diff, $state = 'raw') {
|
||||
$states = array(
|
||||
0 => NULL,
|
||||
1 => NULL,
|
||||
);
|
||||
if (isset($diff['#states'][$state])) {
|
||||
if (isset($diff['#states'][$state]['#old'])) {
|
||||
$states[0] = $diff['#states'][$state]['#old'];
|
||||
}
|
||||
if (isset($diff['#states'][$state]['#new'])) {
|
||||
$states[1] = $diff['#states'][$state]['#new'];
|
||||
}
|
||||
}
|
||||
return $states;
|
||||
}
|
12
sites/all/modules/contrib/content/diff/diff.info
Normal file
12
sites/all/modules/contrib/content/diff/diff.info
Normal file
@@ -0,0 +1,12 @@
|
||||
name = Diff
|
||||
description = Show differences between content revisions.
|
||||
core = 7.x
|
||||
|
||||
files[] = DiffEngine.php
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-13
|
||||
version = "7.x-3.2"
|
||||
core = "7.x"
|
||||
project = "diff"
|
||||
datestamp = "1352784357"
|
||||
|
124
sites/all/modules/contrib/content/diff/diff.install
Normal file
124
sites/all/modules/contrib/content/diff/diff.install
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides uninstallation functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function diff_uninstall() {
|
||||
// Bulk delete entity based variables.
|
||||
$prefixes = array(
|
||||
'diff_enable_revisions_page_',
|
||||
'diff_show_',
|
||||
'diff_view_mode_',
|
||||
'diff_admin_path_',
|
||||
'diff_default_state_',
|
||||
);
|
||||
foreach ($prefixes as $prefix) {
|
||||
db_delete('variable')
|
||||
->condition('name', db_like($prefix) . '%', 'LIKE')
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Delete global variables.
|
||||
variable_del('diff_context_lines_trailing');
|
||||
variable_del('diff_context_lines_leading');
|
||||
variable_del('diff_theme');
|
||||
variable_del('diff_radio_behavior', '');
|
||||
|
||||
foreach (field_info_fields() as $field) {
|
||||
variable_del("diff_{$field['module']}_field_{$field['type']}_default_options");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the existing system variables to target the entity type and bundle.
|
||||
*/
|
||||
function diff_update_7300() {
|
||||
$node_types = array_keys(node_type_get_types());
|
||||
foreach ($node_types as $bundle) {
|
||||
$type_variables = array(
|
||||
'show_preview_changes',
|
||||
'enable_revisions_page',
|
||||
'show_diff_inline',
|
||||
);
|
||||
foreach ($type_variables as $prefix) {
|
||||
$setting = variable_get($prefix . '_' . $bundle, NULL);
|
||||
if (isset($setting)) {
|
||||
variable_del($prefix . '_' . $bundle);
|
||||
variable_set('diff_' . $prefix . '_node_' . $bundle, $setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removed diff_update_7301().
|
||||
*/
|
||||
|
||||
/**
|
||||
* Removed diff_update_7302().
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renames some internal settings names.
|
||||
*/
|
||||
function diff_update_7303() {
|
||||
// Get current values
|
||||
$radio = variable_get('diff_script_revisioning', 'simple');
|
||||
$leading = variable_get('diff_leading_context_lines', 2);
|
||||
$trailing = variable_get('diff_trailing_context_lines', 2);
|
||||
// Create new variable names
|
||||
variable_set('diff_radio_behavior', $radio);
|
||||
variable_set('diff_context_lines_leading', $leading);
|
||||
variable_set('diff_context_lines_trailing', $trailing);
|
||||
// Delete old variables
|
||||
variable_del('diff_script_revisioning');
|
||||
variable_del('diff_leading_context_lines');
|
||||
variable_del('diff_trailing_context_lines');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes unused variable settings and merges inline diff block settings.
|
||||
*/
|
||||
function diff_update_7304() {
|
||||
// This is now always applied to text fields.
|
||||
variable_del('diff_normalise_text');
|
||||
|
||||
// Merge the content type settings for the inline diff block into a single variable.
|
||||
// diff_update_7300() - show_diff_inline_TYPE to diff_show_diff_inline_node_TYPE
|
||||
$node_types = array_keys(node_type_get_types());
|
||||
$enabled_types = array();
|
||||
foreach ($node_types as $node_type) {
|
||||
if (variable_get('diff_show_diff_inline_node_' . $node_type, FALSE)) {
|
||||
$enabled_types[$node_type] = $node_type;
|
||||
}
|
||||
variable_del('diff_show_diff_inline_node_' . $node_type);
|
||||
}
|
||||
variable_set('diff_show_diff_inline_node_bundles', $enabled_types);
|
||||
|
||||
// Warn users that these settings are altered.
|
||||
drupal_set_message(t('Diff <em>Inline differences</em> content type settings are now located within the <em>Inline differences</em> block settings.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates to normalize the new view mode settings.
|
||||
*/
|
||||
function diff_update_7305() {
|
||||
// Rebuild the menus.
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
|
||||
// Removed the enforced entity view mode.
|
||||
db_delete('variable')
|
||||
->condition('name', db_like('diff_view_mode_standard_node_') . '%', 'LIKE')
|
||||
->execute();
|
||||
|
||||
// Removes the configurable view mode for the inline diff block, as this
|
||||
// is fairly meaningless and confusing to users.
|
||||
db_delete('variable')
|
||||
->condition('name', db_like('diff_view_mode_inline_') . '%', 'LIKE')
|
||||
->execute();
|
||||
}
|
623
sites/all/modules/contrib/content/diff/diff.module
Normal file
623
sites/all/modules/contrib/content/diff/diff.module
Normal file
@@ -0,0 +1,623 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides functionality to show a diff between two node revisions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Number of items on one page of the revision list.
|
||||
*/
|
||||
define('REVISION_LIST_SIZE', 50);
|
||||
|
||||
/**
|
||||
* Exposed sorting options.
|
||||
*
|
||||
* No sorting means sorting by delta value for fields.
|
||||
*/
|
||||
define('DIFF_SORT_NONE', '0');
|
||||
|
||||
/**
|
||||
* Exposed sorting options.
|
||||
*
|
||||
* This normally sorts by the rendered comparison.
|
||||
*/
|
||||
define('DIFF_SORT_VALUE', '1');
|
||||
|
||||
/**
|
||||
* Exposed sorting options.
|
||||
*
|
||||
* It is up to the field / entity to decide how to handle the sort.
|
||||
*/
|
||||
define('DIFF_SORT_CUSTOM', '-1');
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function diff_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/help#diff':
|
||||
$output = '<p>' . t('The Diff module replaces the normal <em>Revisions</em> node tab. Diff enhances the listing of revisions with an option to view the differences between any two content revisions. Access to this feature is controlled with the <em>View revisions</em> permission. The feature can be disabled for an entire content type on the content type configuration page. Diff also provides an optional <em>View changes</em> button while editing a node.') . '</p>';
|
||||
return $output;
|
||||
case 'node/%/revisions/%/view':
|
||||
// The translated strings should match node_help('node/%/revisions').
|
||||
return '<p>' . t('Revisions allow you to track differences between multiple versions of your content, and revert back to older versions.') . '</p>';
|
||||
case 'node/%/revisions/view/%/%':
|
||||
return '<p>' . t('Comparing two revisions:') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The various states that are available.
|
||||
*/
|
||||
function diff_available_states($entity_type = NULL) {
|
||||
$states = array(
|
||||
'raw' => t('Standard'),
|
||||
'raw_plain' => t('Marked down'),
|
||||
);
|
||||
|
||||
return $states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*
|
||||
* @todo: Review this.
|
||||
*/
|
||||
function diff_menu() {
|
||||
/*
|
||||
* By using MENU_LOCAL_TASK (and 'tab_parent') we can get the various
|
||||
* revision-views to show the View|Edit|Revision-tabs of the node on top,
|
||||
* and have the Revisions-tab open. To avoid creating/showing any extra tabs
|
||||
* or sub-tabs (tasks below top level) for the various paths (i.e. "Diff",
|
||||
* "Show latest" and "Show a specific revision") that need a revision-id (vid)
|
||||
* parameter, we make sure to set 'tab_parent' a bit odd. This solution may
|
||||
* not be the prettiest one, but by avoiding having two _LOCAL_TASKs sharing
|
||||
* a parent that can be accessed by its full path, it seems to work as
|
||||
* desired. Breadcrumbs work decently, at least the node link is among the
|
||||
* crumbs. For some reason any breadcrumbs "before/above" the node is only
|
||||
* seen at 'node/%node/revisions/%/view'.
|
||||
*/
|
||||
|
||||
// Not used directly, but was created to get the other menu items to work.
|
||||
$items['node/%node/revisions/list'] = array(
|
||||
'title' => 'List revisions',
|
||||
'page callback' => 'diff_diffs_overview',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'access callback' => 'diff_node_revision_access',
|
||||
'access arguments' => array(1),
|
||||
'file' => 'diff.pages.inc',
|
||||
);
|
||||
$items['node/%node/revisions/view'] = array(
|
||||
'title' => 'Compare revisions',
|
||||
'page callback' => 'diff_diffs_show',
|
||||
'page arguments' => array(1, 4, 5, 6),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'access callback' => 'diff_node_revision_access',
|
||||
'access arguments' => array(1),
|
||||
'tab_parent' => 'node/%/revisions/list',
|
||||
'file' => 'diff.pages.inc',
|
||||
);
|
||||
|
||||
$items['node/%node/revisions/view/latest'] = array(
|
||||
'title' => 'Show latest difference',
|
||||
'page callback' => 'diff_latest',
|
||||
'page arguments' => array(1),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'access callback' => 'diff_node_revision_access',
|
||||
'access arguments' => array(1),
|
||||
'tab_parent' => 'node/%/revisions/view',
|
||||
'file' => 'diff.pages.inc',
|
||||
);
|
||||
|
||||
// Administrative settings.
|
||||
$items['admin/config/content/diff'] = array(
|
||||
'title' => 'Diff',
|
||||
'description' => 'Diff settings.',
|
||||
'file' => 'diff.admin.inc',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('diff_admin_settings'),
|
||||
'access arguments' => array('administer site configuration'),
|
||||
);
|
||||
$items['admin/config/content/diff/settings'] = array(
|
||||
'title' => 'Settings',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'weight' => -10,
|
||||
);
|
||||
$items['admin/config/content/diff/fields'] = array(
|
||||
'title' => 'Fields',
|
||||
'description' => 'Field support and settings overview.',
|
||||
'file' => 'diff.admin.inc',
|
||||
'page callback' => 'diff_admin_field_overview',
|
||||
'access arguments' => array('administer site configuration'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
$items['admin/config/content/diff/fields/%'] = array(
|
||||
'title' => 'Global field settings',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('diff_admin_global_field_settings', 5),
|
||||
'access arguments' => array('administer site configuration'),
|
||||
'type' => MENU_VISIBLE_IN_BREADCRUMB,
|
||||
'file' => 'diff.admin.inc',
|
||||
);
|
||||
|
||||
$items['admin/config/content/diff/entities'] = array(
|
||||
'title' => 'Entities',
|
||||
'description' => 'Entity settings.',
|
||||
'file' => 'diff.admin.inc',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('diff_admin_global_entity_settings', 'node'),
|
||||
'access arguments' => array('administer site configuration'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
);
|
||||
|
||||
$items['admin/config/content/diff/entities/node'] = array(
|
||||
'title' => 'Node',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'weight' => -10,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_alter().
|
||||
*/
|
||||
function diff_menu_alter(&$callbacks) {
|
||||
// Overwrite the default 'Revisions' page.
|
||||
$callbacks['node/%node/revisions']['page callback'] = 'diff_diffs_overview';
|
||||
$callbacks['node/%node/revisions']['module'] = 'diff';
|
||||
$callbacks['node/%node/revisions']['file'] = 'diff.pages.inc';
|
||||
|
||||
$callbacks['node/%node/revisions/%/view']['tab_parent'] = 'node/%/revisions/list';
|
||||
$callbacks['node/%node/revisions/%/revert']['tab_parent'] = 'node/%/revisions/%/view';
|
||||
$callbacks['node/%node/revisions/%/delete']['tab_parent'] = 'node/%/revisions/%/view';
|
||||
|
||||
$callbacks['node/%node/revisions']['access callback']
|
||||
= $callbacks['node/%node/revisions/%/view']['access callback']
|
||||
= $callbacks['node/%node/revisions/%/revert']['access callback']
|
||||
= $callbacks['node/%node/revisions/%/delete']['access callback'] = 'diff_node_revision_access';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_admin_paths_alter().
|
||||
*/
|
||||
function diff_admin_paths_alter(&$paths) {
|
||||
// By default, treat all diff pages as administrative.
|
||||
if (variable_get('diff_admin_path_node', 1)) {
|
||||
$paths['node/*/revisions/view/*/*'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for the node revisions page.
|
||||
*/
|
||||
function diff_node_revision_access($node, $op = 'view') {
|
||||
$may_revision_this_type = variable_get('diff_enable_revisions_page_node_' . $node->type, TRUE) || user_access('administer nodes');
|
||||
return $may_revision_this_type && _node_revision_access($node, $op);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_hook_info().
|
||||
*/
|
||||
function diff_hook_info() {
|
||||
$hooks['entity_diff'] = array(
|
||||
'group' => 'diff',
|
||||
);
|
||||
$hooks['diff'] = array(
|
||||
'group' => 'diff',
|
||||
);
|
||||
$hooks['field_diff_view_prepare_alter'] = array(
|
||||
'group' => 'diff',
|
||||
);
|
||||
$hooks['field_diff_view_alter'] = array(
|
||||
'group' => 'diff',
|
||||
);
|
||||
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info_alter().
|
||||
*
|
||||
* Although the module only provides an UI for comparing nodes, it has an
|
||||
* extendable API for any entity, so we supply two view modes for all entities.
|
||||
* - diff_standard: This view mode is used to tell the module how to compare
|
||||
* individual fields. This is used on the revisions page.
|
||||
*/
|
||||
function diff_entity_info_alter(&$entity_info) {
|
||||
foreach (array_keys($entity_info) as $entity_type) {
|
||||
if (!empty($entity_info[$entity_type]['view modes'])) {
|
||||
$entity_info[$entity_type]['view modes'] += array(
|
||||
'diff_standard' => array(
|
||||
'label' => t('Revision comparison'),
|
||||
'custom settings' => FALSE,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_info().
|
||||
*/
|
||||
function diff_block_info() {
|
||||
return array(
|
||||
'inline' => array(
|
||||
'info' => t('Inline differences'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_configure().
|
||||
*/
|
||||
function diff_block_configure($delta = '') {
|
||||
$form = array();
|
||||
switch ($delta) {
|
||||
case 'inline':
|
||||
$form['bundles'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Enabled content types'),
|
||||
'#default_value' => variable_get('diff_show_diff_inline_node_bundles', array()),
|
||||
'#options' => node_type_get_names(),
|
||||
'#description' => t('Show this block only on pages that display content of the given type(s).'),
|
||||
);
|
||||
break;
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_save().
|
||||
*/
|
||||
function diff_block_save($delta = '', $edit = array()) {
|
||||
switch ($delta) {
|
||||
case 'inline':
|
||||
variable_set('diff_show_diff_inline_node_bundles', $edit['bundles']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_view().
|
||||
*/
|
||||
function diff_block_view($delta) {
|
||||
if ($delta === 'inline' && user_access('view revisions') && ($node = menu_get_object()) && arg(2) !== 'edit') {
|
||||
$enabled_types = variable_get('diff_show_diff_inline_node_bundles', array());
|
||||
if (!empty($enabled_types[$node->type])) {
|
||||
$block = array();
|
||||
$revisions = node_revision_list($node);
|
||||
if (count($revisions) > 1) {
|
||||
$block['subject'] = t('Highlight changes');
|
||||
$block['content'] = drupal_get_form('diff_inline_form', $node, $revisions);
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_view_alter().
|
||||
*/
|
||||
function diff_node_view_alter(&$build) {
|
||||
$node = $build['#node'];
|
||||
if (user_access('view revisions') && in_array($node->type, variable_get('diff_show_diff_inline_node_bundles', array()))) {
|
||||
// Ugly but cheap way to check that we are viewing a node's revision page.
|
||||
if (arg(2) === 'revisions' && arg(3) === $node->vid) {
|
||||
module_load_include('inc', 'diff', 'diff.pages');
|
||||
$old_vid = _diff_get_previous_vid(node_revision_list($node), $node->vid);
|
||||
$build = array('#markup' => diff_inline_show($node, $old_vid));
|
||||
}
|
||||
$build['#prefix'] = isset($build['#prefix']) ? "<div id='diff-inline-{$node->nid}'>" . $build['#prefix'] : "<div id='diff-inline-{$node->nid}'>";
|
||||
$build['#suffix'] = isset($build['#suffix']) ? $build['#suffix'] . "</div>" : "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter().
|
||||
*/
|
||||
function diff_form_node_form_alter(&$form, $form_state) {
|
||||
// Add a 'View changes' button on the node edit form.
|
||||
$node = $form['#node'];
|
||||
if (variable_get('diff_show_preview_changes_node_' . $node->type, TRUE) && !empty($node->nid)) {
|
||||
$form['actions']['preview_changes'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('View changes'),
|
||||
'#weight' => 12,
|
||||
'#submit' => array('diff_node_form_build_preview_changes'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter().
|
||||
*/
|
||||
function diff_form_node_type_form_alter(&$form, $form_state) {
|
||||
if (isset($form['type'])) {
|
||||
$type = $form['#node_type'];
|
||||
$form['diff'] = array(
|
||||
'#title' => t('Compare revisions'),
|
||||
'#type' => 'fieldset',
|
||||
'#group' => 'additional_settings',
|
||||
'#tree' => FALSE,
|
||||
);
|
||||
$form['diff']['diff_show_preview_changes_node'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show <em>View changes</em> button on node edit form'),
|
||||
'#weight' => 10,
|
||||
'#default_value' => variable_get('diff_show_preview_changes_node_' . $type->type, TRUE),
|
||||
);
|
||||
$form['diff']['diff_enable_revisions_page_node'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable the <em>Revisions</em> page for this content type'),
|
||||
'#weight' => 11,
|
||||
'#default_value' => variable_get('diff_enable_revisions_page_node_' . $type->type, TRUE),
|
||||
);
|
||||
$options = array();
|
||||
$info = entity_get_info('node');
|
||||
foreach ($info['view modes'] as $view_mode => $view_mode_info) {
|
||||
$options[$view_mode] = $view_mode_info['label'];
|
||||
}
|
||||
$form['diff']['diff_view_mode_preview_node'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Standard comparison preview'),
|
||||
'#description' => t('Governs the <em>Current revision</em> view mode when doing standard comparisons.'),
|
||||
'#options' => $options,
|
||||
'#weight' => 13,
|
||||
'#default_value' => variable_get('diff_view_mode_preview_node_' . $type->type, 'full'),
|
||||
'#empty_value' => '',
|
||||
'#empty_option' => t('- Do not display -'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_type_update().
|
||||
*
|
||||
* This tracks the diff settings in case the node content type is renamed.
|
||||
*/
|
||||
function diff_node_type_update($info) {
|
||||
if (!empty($info->old_type) && $info->old_type != $info->type) {
|
||||
$type_variables = array(
|
||||
'diff_show_preview_changes_node',
|
||||
'diff_enable_revisions_page_node',
|
||||
'diff_view_mode_preview_node',
|
||||
);
|
||||
foreach ($type_variables as $prefix) {
|
||||
$setting = variable_get($prefix . '_' . $info->old_type, NULL);
|
||||
if (isset($setting)) {
|
||||
variable_del($prefix . '_' . $info->old_type);
|
||||
variable_set($prefix . '_' . $info->type, $setting);
|
||||
}
|
||||
}
|
||||
|
||||
// Block settings are combined in a single variable.
|
||||
$inline_block_types = variable_get('diff_show_diff_inline_node_bundles', array());
|
||||
if (isset($inline_block_types[$info->old_type])) {
|
||||
if (!empty($inline_block_types[$info->old_type])) {
|
||||
$inline_block_types[$info->type] = $info->type;
|
||||
}
|
||||
unset($inline_block_types[$info->old_type]);
|
||||
variable_set('diff_show_diff_inline_node_bundles', $inline_block_types);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_type_delete().
|
||||
*/
|
||||
function diff_node_type_delete($info) {
|
||||
variable_del('diff_show_preview_changes_node_' . $info->type);
|
||||
variable_del('diff_enable_revisions_page_node_' . $info->type);
|
||||
variable_del('diff_view_mode_preview_node_' . $info->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the 'View changes' action.
|
||||
*
|
||||
* @see node_form_build_preview()
|
||||
*/
|
||||
function diff_node_form_build_preview_changes($form, &$form_state) {
|
||||
module_load_include('inc', 'diff', 'diff.pages');
|
||||
$old_node = clone node_load($form_state['values']['nid']);
|
||||
$node = node_form_submit_build_node($form, $form_state);
|
||||
|
||||
// Create diff of old node and edited node.
|
||||
$rows = _diff_body_rows($old_node, $node);
|
||||
|
||||
$header = _diff_default_header(t('Original'), t('Changes'));
|
||||
$changes = theme('table__diff__preview', array(
|
||||
'header' => $header,
|
||||
'rows' => $rows,
|
||||
'attributes' => array('class' => 'diff'),
|
||||
'colgroups' => _diff_default_cols(),
|
||||
'sticky' => FALSE,
|
||||
));
|
||||
|
||||
// Prepend diff to edit form.
|
||||
$form_state['node_preview'] = $changes;
|
||||
$form_state['rebuild'] = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function diff_theme() {
|
||||
return array(
|
||||
'diff_node_revisions' => array(
|
||||
'render element' => 'form',
|
||||
'file' => 'diff.theme.inc',
|
||||
),
|
||||
'diff_header_line' => array(
|
||||
'arguments' => array('lineno' => NULL),
|
||||
'file' => 'diff.theme.inc',
|
||||
),
|
||||
'diff_content_line' => array(
|
||||
'arguments' => array('line' => NULL),
|
||||
'file' => 'diff.theme.inc',
|
||||
),
|
||||
'diff_empty_line' => array(
|
||||
'arguments' => array('line' => NULL),
|
||||
'file' => 'diff.theme.inc',
|
||||
),
|
||||
'diff_inline_form' => array(
|
||||
'render element' => 'form',
|
||||
'file' => 'diff.theme.inc',
|
||||
),
|
||||
'diff_inline_metadata' => array(
|
||||
'arguments' => array('node' => NULL),
|
||||
'file' => 'diff.theme.inc',
|
||||
),
|
||||
'diff_inline_chunk' => array(
|
||||
'arguments' => array('text' => '', 'type' => NULL),
|
||||
'file' => 'diff.theme.inc',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the table rows for theme('table').
|
||||
*
|
||||
* @param string $a
|
||||
* The source string to compare from.
|
||||
* @param string $b
|
||||
* The target string to compare to.
|
||||
* @param boolean $show_header
|
||||
* Display diff context headers. For example, "Line x".
|
||||
* @param array $line_stats
|
||||
* This structure tracks line numbers across multiple calls to DiffFormatter.
|
||||
*
|
||||
* @return array
|
||||
* Array of rows usable with theme('table').
|
||||
*/
|
||||
function diff_get_rows($a, $b, $show_header = FALSE, &$line_stats = NULL) {
|
||||
$a = is_array($a) ? $a : explode("\n", $a);
|
||||
$b = is_array($b) ? $b : explode("\n", $b);
|
||||
|
||||
if (!isset($line_stats)) {
|
||||
$line_stats = array(
|
||||
'counter' => array('x' => 0, 'y' => 0),
|
||||
'offset' => array('x' => 0, 'y' => 0),
|
||||
);
|
||||
}
|
||||
$formatter = new DrupalDiffFormatter();
|
||||
// Header is the line counter.
|
||||
$formatter->show_header = $show_header;
|
||||
$formatter->line_stats = &$line_stats;
|
||||
$diff = new Diff($a, $b);
|
||||
return $formatter->format($diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render and markup a diff of two strings into HTML markup.
|
||||
*
|
||||
* @param string $a
|
||||
* The source string to compare from.
|
||||
* @param string $b
|
||||
* The target string to compare to.
|
||||
*
|
||||
* @return string
|
||||
* String containing HTML markup.
|
||||
*/
|
||||
function diff_get_inline($a, $b) {
|
||||
$diff = new DrupalDiffInline($a, $b);
|
||||
return $diff->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Form builder: Inline diff controls.
|
||||
*/
|
||||
function diff_inline_form($form, $form_state, $node, $revisions) {
|
||||
$form = array();
|
||||
$form['node'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $node,
|
||||
);
|
||||
$form['revision'] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => array(0 => t('- No highlighting -')),
|
||||
'#default_value' => (arg(2) === 'revisions' && arg(3) === $node->vid) ? $node->vid : 0,
|
||||
'#ajax' => array(
|
||||
'callback' => 'diff_inline_ajax',
|
||||
'wrapper' => "node-{$node->nid}",
|
||||
'method' => 'replace',
|
||||
),
|
||||
);
|
||||
foreach ($revisions as $revision) {
|
||||
$form['revision']['#options'][$revision->vid] = t('@revision by @name', array(
|
||||
'@revision' => format_date($revision->timestamp, 'short'),
|
||||
'@name' => format_username($revision),
|
||||
));
|
||||
}
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('View'),
|
||||
'#submit' => array('diff_inline_form_submit'),
|
||||
'#attributes' => array('class' => array('diff-js-hidden')),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* AHAH callback for rendering the inline diff of a node.
|
||||
*/
|
||||
function diff_inline_ajax($form, $form_state) {
|
||||
module_load_include('inc', 'diff', 'diff.pages');
|
||||
$node = $form['node']['#value'];
|
||||
$vid = isset($form_state['values']['revision']) ? $form_state['values']['revision'] : 0;
|
||||
return "<div id='node-{$node->nid}'>" . diff_inline_show($node, $vid) . "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for diff_inline_form() for JS-disabled clients.
|
||||
*/
|
||||
function diff_inline_form_submit(&$form, &$form_state) {
|
||||
if (isset($form_state['values']['revision'], $form_state['values']['node'])) {
|
||||
$node = $form_state['values']['node'];
|
||||
$vid = $form_state['values']['revision'];
|
||||
$form_state['redirect'] = "node/{$node->nid}/revisions/{$vid}/view";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to normalise system differences.
|
||||
*
|
||||
* This handles differences in:
|
||||
* - line endings: Mac, Windows and UNIX all use different line endings.
|
||||
*/
|
||||
function diff_normalise_text($text) {
|
||||
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper function for filter_xss() to exclude all tags.
|
||||
*/
|
||||
function diff_filter_xss($string) {
|
||||
return filter_xss($string, array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to load any CSS or JScript files required by a page or form.
|
||||
*/
|
||||
function diff_build_attachments($jscript = FALSE) {
|
||||
$attachments = array();
|
||||
$theme = variable_get('diff_theme', 'default');
|
||||
if ($theme) {
|
||||
$attachments['css'] = array(
|
||||
drupal_get_path('module', 'diff') . "/css/diff.{$theme}.css",
|
||||
);
|
||||
}
|
||||
$type = variable_get('diff_radio_behavior', 'simple');
|
||||
if ($jscript && $type) {
|
||||
$attachments['js'] = array(
|
||||
drupal_get_path('module', 'diff') . "/js/diff.js",
|
||||
array(
|
||||
'data' => array('diffRevisionRadios' => $type),
|
||||
'type' => 'setting',
|
||||
),
|
||||
);
|
||||
}
|
||||
return $attachments;
|
||||
}
|
632
sites/all/modules/contrib/content/diff/diff.pages.inc
Normal file
632
sites/all/modules/contrib/content/diff/diff.pages.inc
Normal file
@@ -0,0 +1,632 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Menu callbacks for hook_menu().
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback - show latest diff for a given node.
|
||||
*/
|
||||
function diff_latest($node) {
|
||||
$revisions = node_revision_list($node);
|
||||
$new = array_shift($revisions);
|
||||
$old = array_shift($revisions);
|
||||
drupal_goto("node/{$node->nid}/revisions/view/{$old->vid}/{$new->vid}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback - an overview table of older revisions.
|
||||
*
|
||||
* Generate an overview table of older revisions of a node and provide
|
||||
* an input form to select two revisions for a comparison.
|
||||
*/
|
||||
function diff_diffs_overview($node) {
|
||||
drupal_set_title(t('Revisions for %title', array('%title' => $node->title)), PASS_THROUGH);
|
||||
return drupal_get_form('diff_node_revisions', $node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Input form to select two revisions.
|
||||
*/
|
||||
function diff_node_revisions($form, $form_state, $node) {
|
||||
$form['nid'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $node->nid,
|
||||
);
|
||||
|
||||
$revision_list = node_revision_list($node);
|
||||
|
||||
if (count($revision_list) > REVISION_LIST_SIZE) {
|
||||
// If the list of revisions is longer than the number shown on one page
|
||||
// split the array.
|
||||
$page = isset($_GET['page']) ? $_GET['page'] : '0';
|
||||
$revision_chunks = array_chunk(node_revision_list($node), REVISION_LIST_SIZE);
|
||||
$revisions = $revision_chunks[$page];
|
||||
// Set up global pager variables as would 'pager_query' do.
|
||||
// These variables are then used in the theme('pager') call later.
|
||||
global $pager_page_array, $pager_total, $pager_total_items;
|
||||
$pager_total_items[0] = count($revision_list);
|
||||
$pager_total[0] = ceil(count($revision_list) / REVISION_LIST_SIZE);
|
||||
$pager_page_array[0] = max(0, min($page, ((int) $pager_total[0]) - 1));
|
||||
}
|
||||
else {
|
||||
$revisions = $revision_list;
|
||||
}
|
||||
|
||||
$revert_permission = FALSE;
|
||||
if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
|
||||
$revert_permission = TRUE;
|
||||
}
|
||||
$delete_permission = FALSE;
|
||||
if ((user_access('delete revisions') || user_access('administer nodes')) && node_access('delete', $node)) {
|
||||
$delete_permission = TRUE;
|
||||
}
|
||||
|
||||
foreach ($revisions as $revision) {
|
||||
$operations = array();
|
||||
$revision_ids[$revision->vid] = '';
|
||||
|
||||
$revision_log = ($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : '';
|
||||
if ($revision->current_vid > 0) {
|
||||
$form['info'][$revision->vid] = array(
|
||||
'#markup' => t('!date by !username', array(
|
||||
'!date' => l(format_date($revision->timestamp, 'small'), "node/$node->nid"),
|
||||
'!username' => theme('username', array('account' => $revision)))) . $revision_log,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$diff_date = l(format_date($revision->timestamp, 'small'), "node/$node->nid/revisions/$revision->vid/view");
|
||||
$form['info'][$revision->vid] = array(
|
||||
'#markup' => t('!date by !username', array(
|
||||
'!date' => $diff_date,
|
||||
'!username' => theme('username', array('account' => $revision)))
|
||||
) . $revision_log,
|
||||
);
|
||||
if ($revert_permission) {
|
||||
$operations[] = array(
|
||||
'#markup' => l(t('Revert'), "node/$node->nid/revisions/$revision->vid/revert"),
|
||||
);
|
||||
}
|
||||
if ($delete_permission) {
|
||||
$operations[] = array(
|
||||
'#markup' => l(t('Delete'), "node/$node->nid/revisions/$revision->vid/delete"),
|
||||
);
|
||||
}
|
||||
// Set a dummy, even if the user has no permission for the other
|
||||
// operations, so that we can check if the operations array is
|
||||
// empty to know if the row denotes the current revision.
|
||||
$operations[] = array();
|
||||
}
|
||||
$form['operations'][$revision->vid] = $operations;
|
||||
|
||||
}
|
||||
$new_vid = key($revision_ids);
|
||||
next($revision_ids);
|
||||
$old_vid = key($revision_ids);
|
||||
$form['diff']['old'] = array(
|
||||
'#type' => 'radios',
|
||||
'#options' => $revision_ids,
|
||||
'#default_value' => $old_vid,
|
||||
);
|
||||
$form['diff']['new'] = array(
|
||||
'#type' => 'radios',
|
||||
'#options' => $revision_ids,
|
||||
'#default_value' => $new_vid,
|
||||
);
|
||||
|
||||
$form['submit'] = array('#type' => 'submit', '#value' => t('Compare'));
|
||||
|
||||
if (count($revision_list) > REVISION_LIST_SIZE) {
|
||||
$form['#suffix'] = theme('pager');
|
||||
}
|
||||
$form['#attached'] = diff_build_attachments(TRUE);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit code for input form to select two revisions.
|
||||
*/
|
||||
function diff_node_revisions_submit($form, &$form_state) {
|
||||
// The ids are ordered so the old revision is always on the left.
|
||||
$old_vid = min($form_state['values']['old'], $form_state['values']['new']);
|
||||
$new_vid = max($form_state['values']['old'], $form_state['values']['new']);
|
||||
$form_state['redirect'] = 'node/' . $form_state['values']['nid'] . '/revisions/view/' . $old_vid . '/' . $new_vid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation for input form to select two revisions.
|
||||
*/
|
||||
function diff_node_revisions_validate($form, &$form_state) {
|
||||
$old_vid = $form_state['values']['old'];
|
||||
$new_vid = $form_state['values']['new'];
|
||||
if ($old_vid == $new_vid || !$old_vid || !$new_vid) {
|
||||
form_set_error('diff', t('Select different revisions to compare.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a comparison for the node between versions 'old_vid' and 'new_vid'.
|
||||
*
|
||||
* @param object $node
|
||||
* Node on which to perform comparison
|
||||
* @param integer $old_vid
|
||||
* Version ID of the old revision.
|
||||
* @param integer $new_vid
|
||||
* Version ID of the new revision.
|
||||
*/
|
||||
function diff_diffs_show($node, $old_vid, $new_vid, $state = NULL) {
|
||||
// Attaches the CSS.
|
||||
$build['#attached'] = diff_build_attachments();
|
||||
|
||||
$default_state = variable_get('diff_default_state_node', 'raw');
|
||||
if (empty($state)) {
|
||||
$state = $default_state;
|
||||
}
|
||||
$state = str_replace('-', '_', $state);
|
||||
if (!array_key_exists($state, diff_available_states())) {
|
||||
$state = $default_state;
|
||||
}
|
||||
|
||||
// Same title as the 'Revisions' tab. Blocked by non-page requests.
|
||||
if (node_is_page($node)) {
|
||||
drupal_set_title(t('Revisions for %title', array('%title' => $node->title)), PASS_THROUGH);
|
||||
}
|
||||
$node_revisions = node_revision_list($node);
|
||||
|
||||
$old_node = node_load($node->nid, $old_vid);
|
||||
$new_node = node_load($node->nid, $new_vid);
|
||||
|
||||
// Generate table header (date, username, log message).
|
||||
$old_header = t('!date by !username', array(
|
||||
'!date' => l(format_date($old_node->revision_timestamp), "node/$node->nid/revisions/$old_node->vid/view", array('absolute' => 1)),
|
||||
'!username' => theme('username', array('account' => $node_revisions[$old_vid])),
|
||||
));
|
||||
$new_header = t('!date by !username', array(
|
||||
'!date' => l(format_date($new_node->revision_timestamp), "node/$node->nid/revisions/$new_node->vid/view", array('absolute' => 1)),
|
||||
'!username' => theme('username', array('account' => $node_revisions[$new_vid])),
|
||||
));
|
||||
|
||||
$old_log = $old_node->log != '' ? '<p class="revision-log">' . filter_xss($old_node->log) . '</p>' : '';
|
||||
$new_log = $new_node->log != '' ? '<p class="revision-log">' . filter_xss($new_node->log) . '</p>' : '';
|
||||
|
||||
// Generate previous diff/next diff links.
|
||||
$nav_suffix = ($default_state != $state) ? '/' . str_replace('_', '-', $state) : '';
|
||||
$next_vid = _diff_get_next_vid($node_revisions, $new_vid);
|
||||
if ($next_vid) {
|
||||
$next_link = l(t('Next difference >'), 'node/' . $node->nid . '/revisions/view/' . $new_vid . '/' . $next_vid . $nav_suffix, array('absolute' => 1));
|
||||
}
|
||||
else {
|
||||
$next_link = '';
|
||||
}
|
||||
$prev_vid = _diff_get_previous_vid($node_revisions, $old_vid);
|
||||
if ($prev_vid) {
|
||||
$prev_link = l(t('< Previous difference'), 'node/' . $node->nid . '/revisions/view/' . $prev_vid . '/' . $old_vid . $nav_suffix, array('absolute' => 1));
|
||||
}
|
||||
else {
|
||||
$prev_link = '';
|
||||
}
|
||||
|
||||
$header = _diff_default_header($old_header, $new_header);
|
||||
$rows = array();
|
||||
if ($old_log || $new_log) {
|
||||
$rows['logs'] = array(
|
||||
array(
|
||||
'data' => $old_log,
|
||||
'colspan' => 2,
|
||||
),
|
||||
array(
|
||||
'data' => $new_log,
|
||||
'colspan' => 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
$rows['navigation'] = array(
|
||||
array(
|
||||
'data' => $prev_link,
|
||||
'class' => array('diff-prevlink'),
|
||||
'colspan' => 2,
|
||||
),
|
||||
array(
|
||||
'data' => $next_link,
|
||||
'class' => array('diff-nextlink'),
|
||||
'colspan' => 2,
|
||||
),
|
||||
);
|
||||
|
||||
$links = array();
|
||||
foreach (diff_available_states('node') as $alternative_state => $label) {
|
||||
if ($alternative_state == $state) {
|
||||
// @todo: Should we show both or just alternatives?
|
||||
}
|
||||
$links[$alternative_state] = array(
|
||||
'title' => $label,
|
||||
'href' => "node/{$node->nid}/revisions/view/{$old_vid}/{$new_vid}" . ($alternative_state == $default_state ? '' : '/' . str_replace('_', '-', $alternative_state)),
|
||||
);
|
||||
}
|
||||
if (count($links) > 1) {
|
||||
$state_links = theme('links', array(
|
||||
'links' => $links,
|
||||
'attributes' => array('class' => array('links', 'inline')),
|
||||
));
|
||||
$rows['states'] = array(
|
||||
array(
|
||||
'data' => $state_links,
|
||||
'class' => 'diff-links',
|
||||
'colspan' => 4,
|
||||
),
|
||||
);
|
||||
}
|
||||
$rows = array_merge($rows, _diff_body_rows($old_node, $new_node, $state));
|
||||
|
||||
$build['diff_table'] = array(
|
||||
'#theme' => 'table__diff__standard',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#attributes' => array('class' => array('diff')),
|
||||
'#colgroups' => _diff_default_cols(),
|
||||
'#sticky' => FALSE,
|
||||
);
|
||||
|
||||
// Allow users to hide or set the display mode of the preview.
|
||||
if (node_is_page($node) && $view_mode = variable_get('diff_view_mode_preview_node_' . $new_node->type, 'full')) {
|
||||
$header = '';
|
||||
if ($node->vid == $new_vid) {
|
||||
$header .= '<div class="diff-section-title">' . t('Current revision:') . '</div>';
|
||||
}
|
||||
else {
|
||||
$header .= '<div class="diff-section-title">' . t('Revision of @new_date:', array('@new_date' => format_date($new_node->revision_timestamp))) . '</div>';
|
||||
}
|
||||
$build['diff_preview']['header']['#markup'] = $header;
|
||||
// Don't include node links or comments when viewing the diff.
|
||||
$build['diff_preview']['content'] = node_view($new_node, $view_mode);
|
||||
if (isset($build['diff_preview']['content']['links'])) {
|
||||
unset($build['diff_preview']['content']['links']);
|
||||
}
|
||||
if (isset($build['diff_preview']['content']['comments'])) {
|
||||
unset($build['diff_preview']['content']['comments']);
|
||||
}
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array of rows which represent the difference between nodes.
|
||||
*
|
||||
* @param object $old_node
|
||||
* Node for comparison which will be displayed on the left side.
|
||||
* @param object $new_node
|
||||
* Node for comparison which will be displayed on the right side.
|
||||
* @param boolean $state
|
||||
* The state to render for the diff.
|
||||
*/
|
||||
function _diff_body_rows($old_node, $new_node, $state = 'raw') {
|
||||
// This is an unique index only, so no need for drupal_static().
|
||||
static $table_row_counter = 0;
|
||||
|
||||
if ($theme = variable_get('diff_theme', 'default')) {
|
||||
drupal_add_css(drupal_get_path('module', 'diff') . "/css/diff.{$theme}.css");
|
||||
}
|
||||
module_load_include('inc', 'diff', 'includes/node');
|
||||
|
||||
$rows = array();
|
||||
$any_visible_change = FALSE;
|
||||
$context = array(
|
||||
'entity_type' => 'node',
|
||||
'states' => array($state),
|
||||
'view_mode' => 'diff_standard',
|
||||
);
|
||||
|
||||
$node_diffs = diff_compare_entities($old_node, $new_node, $context);
|
||||
|
||||
// Track line numbers between multiple diffs.
|
||||
$line_stats = array(
|
||||
'counter' => array('x' => 0, 'y' => 0),
|
||||
'offset' => array('x' => 0, 'y' => 0),
|
||||
);
|
||||
|
||||
// Render diffs for each.
|
||||
foreach ($node_diffs as $node_diff) {
|
||||
$show_header = !empty($node_diff['#name']);
|
||||
// These are field level settings.
|
||||
if ($show_header && isset($node_diff['#settings']['show_header'])) {
|
||||
$show_header = $show_header && $node_diff['#settings']['show_header'];
|
||||
}
|
||||
|
||||
// Line counting and line header options.
|
||||
if (empty($node_diff['#settings']['line_counter'])) {
|
||||
$line_counter = FALSE;
|
||||
}
|
||||
else {
|
||||
$line_counter = $node_diff['#settings']['line_counter'];
|
||||
}
|
||||
// Every call to 'line' resets the counters.
|
||||
if ($line_counter) {
|
||||
$line_stats['counter']['x'] = 0;
|
||||
$line_stats['counter']['y'] = 0;
|
||||
if ($line_counter == 'line' && 0) {
|
||||
$line_stats['offset']['x'] = 0;
|
||||
$line_stats['offset']['y'] = 0;
|
||||
}
|
||||
$line_stats_ref = $line_stats;
|
||||
}
|
||||
else {
|
||||
$line_stats_ref = NULL;
|
||||
}
|
||||
|
||||
list($old, $new) = diff_extract_state($node_diff, $state);
|
||||
if ($node_diff_rows = diff_get_rows($old, $new, $line_counter && $line_counter != 'hidden', $line_stats_ref)) {
|
||||
if ($line_counter && $line_counter != 'line') {
|
||||
$line_stats['offset']['x'] += $line_stats_ref['counter']['x'];
|
||||
$line_stats['offset']['y'] += $line_stats_ref['counter']['y'];
|
||||
}
|
||||
if ($show_header) {
|
||||
$rows['diff-header-' . $table_row_counter++] = array(
|
||||
array(
|
||||
'data' => t('Changes to %name', array('%name' => $node_diff['#name'])),
|
||||
'class' => 'diff-section-title',
|
||||
'colspan' => 4,
|
||||
),
|
||||
);
|
||||
}
|
||||
// To avoid passing counter to the Diff engine, index rows manually here
|
||||
// to allow modules to interact with the table. i.e. no array_merge().
|
||||
foreach ($node_diff_rows as $row) {
|
||||
$rows['diff-row-' . $table_row_counter++] = $row;
|
||||
}
|
||||
$any_visible_change = TRUE;
|
||||
}
|
||||
}
|
||||
if (!$any_visible_change) {
|
||||
$rows['diff-empty-' . $table_row_counter++] = array(
|
||||
array(
|
||||
'data' => t('No visible changes'),
|
||||
'class' => 'diff-section-title',
|
||||
'colspan' => 4,
|
||||
),
|
||||
);
|
||||
// @todo: revise this.
|
||||
// Needed to keep safari happy.
|
||||
$rows['diff-empty-' . $table_row_counter++] = array(
|
||||
array('data' => ''),
|
||||
array('data' => ''),
|
||||
array('data' => ''),
|
||||
array('data' => ''),
|
||||
);
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic callback to compare two entities.
|
||||
*/
|
||||
function diff_compare_entities($left_entity, $right_entity, $context) {
|
||||
$entity_type = $context['entity_type'];
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $right_entity);
|
||||
$context['bundle'] = $bundle;
|
||||
$context['old_entity'] = $left_entity;
|
||||
$context['new_entity'] = $right_entity;
|
||||
$context += array(
|
||||
'states' => array('raw'),
|
||||
'view_mode' => FALSE,
|
||||
'language' => LANGUAGE_NONE,
|
||||
);
|
||||
|
||||
$diff = module_invoke_all('entity_diff', $left_entity, $right_entity, $context);
|
||||
|
||||
// Allow other modules to interact directly with the results.
|
||||
drupal_alter('entity_diff', $diff, $context);
|
||||
|
||||
// We start off assuming all form elements are in the correct order.
|
||||
$diff['#sorted'] = TRUE;
|
||||
|
||||
// Field rows. Recurse through all child elements.
|
||||
$count = 0;
|
||||
foreach (element_children($diff) as $key) {
|
||||
if (!isset($diff[$key]['#states'])) {
|
||||
$diff[$key]['#states'] = array();
|
||||
}
|
||||
|
||||
// Ensure that the element follows the new #states format.
|
||||
if (isset($diff[$key]['#old'])) {
|
||||
$diff[$key]['#states']['raw']['#old'] = $diff[$key]['#old'];
|
||||
unset($diff[$key]['#old']);
|
||||
}
|
||||
if (isset($diff[$key]['#new'])) {
|
||||
$diff[$key]['#states']['raw']['#new'] = $diff[$key]['#new'];
|
||||
unset($diff[$key]['#new']);
|
||||
}
|
||||
|
||||
// If requested, we can convert the .
|
||||
foreach (array('raw', 'rendered') as $state) {
|
||||
if (in_array($state . '_plain', $context['states'])) {
|
||||
diff_markdown_state($diff[$key], $state);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign a decimal placeholder weight to preserve original array order.
|
||||
if (!isset($diff[$key]['#weight'])) {
|
||||
$diff[$key]['#weight'] = $count / 1000;
|
||||
}
|
||||
else {
|
||||
// If one child element has a weight then we will need to sort later.
|
||||
unset($diff['#sorted']);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
|
||||
// One of the children has a #weight.
|
||||
if (!isset($diff['#sorted'])) {
|
||||
uasort($diff, 'element_sort');
|
||||
}
|
||||
|
||||
// Process the array and get line counts per field.
|
||||
array_walk($diff, 'diff_process_state_lines');
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
function diff_process_state_lines(&$diff, $key) {
|
||||
foreach ($diff['#states'] as $state => $data) {
|
||||
if (isset($data['#old'])) {
|
||||
if (is_string($data['#old'])) {
|
||||
$diff['#states'][$state]['#old'] = explode("\n", $data['#old']);
|
||||
}
|
||||
$diff['#states'][$state]['#count_old'] = count($diff['#states'][$state]['#old']);
|
||||
}
|
||||
else {
|
||||
$diff['#states'][$state]['#count_old'] = 0;
|
||||
}
|
||||
if (isset($data['#new'])) {
|
||||
if (is_string($data['#new'])) {
|
||||
$diff['#states'][$state]['#new'] = explode("\n", $data['#new']);
|
||||
}
|
||||
$diff['#states'][$state]['#count_new'] = count($diff['#states'][$state]['#new']);
|
||||
}
|
||||
else {
|
||||
$diff['#states'][$state]['#count_new'] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to render plain states from the corresponding raw state.
|
||||
*
|
||||
* @param array $diff
|
||||
* The Diff Engine output array.
|
||||
* @param string $state
|
||||
* The state to markdown.
|
||||
*/
|
||||
function diff_markdown_state(&$diff, $state) {
|
||||
list($plain_old, $plain_new) = diff_extract_state($diff, $state . '_plain');
|
||||
list($old, $new) = diff_extract_state($diff, $state);
|
||||
$markdown = FALSE;
|
||||
if (isset($diff['#settings']) && !empty($diff['#settings']['markdown'])) {
|
||||
if (function_exists($diff['#settings']['markdown'])) {
|
||||
$markdown = $diff['#settings']['markdown'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($plain_old) && isset($old)) {
|
||||
if (is_array($old)) {
|
||||
$diff['#states'][$state . '_plain']['#old'] = $markdown ? array_map($markdown, $old) : $old;
|
||||
}
|
||||
else {
|
||||
$diff['#states'][$state . '_plain']['#old'] = $markdown ? $markdown($old) : $old;
|
||||
}
|
||||
}
|
||||
if (!isset($plain_new) && isset($new)) {
|
||||
if (is_array($new)) {
|
||||
$diff['#states'][$state . '_plain']['#new'] = $markdown ? array_map($markdown, $new) : $new;
|
||||
}
|
||||
else {
|
||||
$diff['#states'][$state . '_plain']['#new'] = $markdown ? $markdown($new) : $new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry in the revisions list after $vid.
|
||||
*
|
||||
* @param array $node_revisions
|
||||
* Array of node revision IDs in descending order.
|
||||
* @param int $vid
|
||||
* Version ID to look for.
|
||||
*
|
||||
* @return boolean|integer
|
||||
* Returns FALSE if $vid is the last entry.
|
||||
*/
|
||||
function _diff_get_next_vid($node_revisions, $vid) {
|
||||
$previous = NULL;
|
||||
foreach ($node_revisions as $revision) {
|
||||
if ($revision->vid == $vid) {
|
||||
return ($previous ? $previous->vid : FALSE);
|
||||
}
|
||||
$previous = $revision;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry in the revision list before $vid.
|
||||
*
|
||||
* @param array $node_revisions
|
||||
* Array of node revision IDs in descending order.
|
||||
* @param integer $vid
|
||||
* Version ID to look for.
|
||||
*
|
||||
* @return boolean|integer
|
||||
* Returns FALSE if $vid is the first entry.
|
||||
*/
|
||||
function _diff_get_previous_vid($node_revisions, $vid) {
|
||||
$previous = NULL;
|
||||
foreach ($node_revisions as $revision) {
|
||||
if ($previous && $previous->vid == $vid) {
|
||||
return $revision->vid;
|
||||
}
|
||||
$previous = $revision;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create default 'cols' array for diff table.
|
||||
*/
|
||||
function _diff_default_cols() {
|
||||
return array(
|
||||
array(
|
||||
array(
|
||||
'class' => 'diff-marker',
|
||||
),
|
||||
array(
|
||||
'class' => 'diff-content',
|
||||
),
|
||||
array(
|
||||
'class' => 'diff-marker',
|
||||
),
|
||||
array(
|
||||
'class' => 'diff-content',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create default 'header' array for diff table.
|
||||
*/
|
||||
function _diff_default_header($old_header = '', $new_header = '') {
|
||||
return array(
|
||||
array(
|
||||
'data' => $old_header,
|
||||
'colspan' => 2,
|
||||
),
|
||||
array(
|
||||
'data' => $new_header,
|
||||
'colspan' => 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the inline diff for a given node, vid.
|
||||
*
|
||||
* If vid = 0 or no previous vid exists for the given revision returns the
|
||||
* normally rendered content of the specified revision.
|
||||
*/
|
||||
function diff_inline_show($node, $vid = 0, $metadata = TRUE) {
|
||||
$new_node = $vid ? node_load($node->nid, $vid, TRUE) : clone $node;
|
||||
node_build_content($new_node);
|
||||
$new = drupal_render($new_node->content);
|
||||
|
||||
$old = $vid ? _diff_get_previous_vid(node_revision_list($node), $vid) : 0;
|
||||
if ($old) {
|
||||
$old_node = node_load($node->nid, $old, TRUE);
|
||||
node_build_content($old_node);
|
||||
$old = drupal_render($old_node->content);
|
||||
$output = $metadata ? theme('diff_inline_metadata', array('node' => $new_node)) : '';
|
||||
$output .= diff_get_inline($old, $new);
|
||||
return $output;
|
||||
}
|
||||
return $new;
|
||||
}
|
149
sites/all/modules/contrib/content/diff/diff.theme.inc
Normal file
149
sites/all/modules/contrib/content/diff/diff.theme.inc
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Themeable function callbacks for diff.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Theme function to display the revisions formular.
|
||||
*/
|
||||
function theme_diff_node_revisions($vars) {
|
||||
$form = $vars['form'];
|
||||
$output = '';
|
||||
|
||||
// Overview table:
|
||||
$header = array(
|
||||
t('Revision'),
|
||||
array('data' => drupal_render($form['submit']), 'colspan' => 2),
|
||||
array('data' => t('Operations'), 'colspan' => 2),
|
||||
);
|
||||
if (isset($form['info']) && is_array($form['info'])) {
|
||||
foreach (element_children($form['info']) as $key) {
|
||||
$row = array();
|
||||
if (isset($form['operations'][$key][0])) {
|
||||
// Note: even if the commands for revert and delete are not permitted,
|
||||
// the array is not empty since we set a dummy in this case.
|
||||
$row[] = drupal_render($form['info'][$key]);
|
||||
$row[] = drupal_render($form['diff']['old'][$key]);
|
||||
$row[] = drupal_render($form['diff']['new'][$key]);
|
||||
$row[] = drupal_render($form['operations'][$key][0]);
|
||||
$row[] = drupal_render($form['operations'][$key][1]);
|
||||
$rows[] = array(
|
||||
'data' => $row,
|
||||
'class' => array('diff-revision'),
|
||||
);
|
||||
}
|
||||
else {
|
||||
// The current revision (no commands to revert or delete).
|
||||
$row[] = array(
|
||||
'data' => drupal_render($form['info'][$key]),
|
||||
'class' => array('revision-current'),
|
||||
);
|
||||
$row[] = array(
|
||||
'data' => drupal_render($form['diff']['old'][$key]),
|
||||
'class' => array('revision-current'),
|
||||
);
|
||||
$row[] = array(
|
||||
'data' => drupal_render($form['diff']['new'][$key]),
|
||||
'class' => array('revision-current'),
|
||||
);
|
||||
$row[] = array(
|
||||
'data' => t('current revision'),
|
||||
'class' => array('revision-current'),
|
||||
'colspan' => '2',
|
||||
);
|
||||
$rows[] = array(
|
||||
'data' => $row,
|
||||
'class' => array('error diff-revision'),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
$output .= theme('table__diff__revisions', array(
|
||||
'header' => $header,
|
||||
'rows' => $rows,
|
||||
'sticky' => FALSE,
|
||||
'attributes' => array('class' => 'diff-revisions'),
|
||||
));
|
||||
|
||||
$output .= drupal_render_children($form);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Theme function for a header line in the diff.
|
||||
*/
|
||||
function theme_diff_header_line($vars) {
|
||||
return '<strong>' . t('Line @lineno', array('@lineno' => $vars['lineno'])) . '</strong>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme function for a content line in the diff.
|
||||
*/
|
||||
function theme_diff_content_line($vars) {
|
||||
return '<div>' . $vars['line'] . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme function for an empty line in the diff.
|
||||
*/
|
||||
function theme_diff_empty_line($vars) {
|
||||
return $vars['line'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme function for inline diff form.
|
||||
*/
|
||||
function theme_diff_inline_form($vars) {
|
||||
if ($theme = variable_get('diff_theme', 'default')) {
|
||||
drupal_add_css(drupal_get_path('module', 'diff') . "/css/diff.{$theme}.css");
|
||||
}
|
||||
return drupal_render_children($vars['form']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display inline diff metadata.
|
||||
*/
|
||||
function theme_diff_inline_metadata($vars) {
|
||||
if ($theme = variable_get('diff_theme', 'default')) {
|
||||
drupal_add_css(drupal_get_path('module', 'diff') . "/css/diff.{$theme}.css");
|
||||
}
|
||||
$node = $vars['node'];
|
||||
|
||||
$output = "<div class='diff-inline-metadata clear-block'>";
|
||||
$output .= "<div class='diff-inline-byline'>";
|
||||
$output .= t('Updated by !name on @date', array(
|
||||
'!name' => theme('username', array('account' => user_load($node->revision_uid))),
|
||||
'@date' => format_date($node->revision_timestamp, 'small'),
|
||||
));
|
||||
$output .= "</div>";
|
||||
$output .= "<div class='diff-inline-legend clear-block'>";
|
||||
$output .= "<label>" . t('Legend') . "</label>";
|
||||
$output .= theme('diff_inline_chunk', array('text' => t('Added'), 'type' => 'add'));
|
||||
$output .= theme('diff_inline_chunk', array('text' => t('Changed'), 'type' => 'change'));
|
||||
$output .= theme('diff_inline_chunk', array('text' => t('Deleted'), 'type' => 'delete'));
|
||||
$output .= "</div>";
|
||||
$output .= "</div>";
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme a span of changed text in an inline diff display.
|
||||
*/
|
||||
function theme_diff_inline_chunk($vars) {
|
||||
switch ($vars['type']) {
|
||||
case 'add':
|
||||
return "<span class='diff-added'>{$vars['text']}</span>";
|
||||
case 'change':
|
||||
return "<span class='diff-changed'>{$vars['text']}</span>";
|
||||
case 'delete':
|
||||
return "<span class='diff-deleted'>{$vars['text']}</span>";
|
||||
default:
|
||||
return $vars['text'];
|
||||
}
|
||||
}
|
63
sites/all/modules/contrib/content/diff/diff.tokens.inc
Normal file
63
sites/all/modules/contrib/content/diff/diff.tokens.inc
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Builds placeholder replacement tokens for diff-related data.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function diff_token_info() {
|
||||
$node['diff'] = array(
|
||||
'name' => t('Latest differences'),
|
||||
'description' => t('The differences between the current revision and the previous revision of this node.'),
|
||||
);
|
||||
$node['diff-markdown'] = array(
|
||||
'name' => t('Latest differences (marked down)'),
|
||||
'description' => t('The differences between the current revision and the previous revision of this node, but with a marked-down form of each revision used for comparison.'),
|
||||
);
|
||||
|
||||
return array(
|
||||
'tokens' => array('node' => $node),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function diff_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$sanitize = !empty($options['sanitize']);
|
||||
|
||||
$replacements = array();
|
||||
|
||||
if ($type == 'node' && !empty($data['node'])) {
|
||||
$node = $data['node'];
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
// Basic diff standard comparison information.
|
||||
case 'diff':
|
||||
case 'diff-markdown':
|
||||
$revisons = node_revision_list($node);
|
||||
if (count($revisons) == 1) {
|
||||
$replacements[$original] = t('(No previous revision available.)');
|
||||
}
|
||||
else {
|
||||
module_load_include('inc', 'diff', 'diff.pages');
|
||||
$old_vid = _diff_get_previous_vid($revisons, $node->vid);
|
||||
$state = $name == 'diff' ? 'raw' : 'raw_plain';
|
||||
$build = diff_diffs_show($node, $old_vid, $node->vid, $state);
|
||||
unset($build['diff_table']['#rows']['states']);
|
||||
unset($build['diff_table']['#rows']['navigation']);
|
||||
unset($build['diff_preview']);
|
||||
|
||||
$output = drupal_render_children($build);
|
||||
$replacements[$original] = $sanitize ? check_plain($output) : $output;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return $replacements;
|
||||
}
|
111
sites/all/modules/contrib/content/diff/includes/file.inc
Normal file
111
sites/all/modules/contrib/content/diff/includes/file.inc
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide diff field functions for the file module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Diff field callback for preloading file entities.
|
||||
*/
|
||||
function file_field_diff_view_prepare(&$old_items, &$new_items, $context) {
|
||||
$fids = array();
|
||||
foreach (array_merge_recursive($old_items, $new_items) as $info) {
|
||||
$fids[$info['fid']] = $info['fid'];
|
||||
}
|
||||
$files = file_load_multiple($fids);
|
||||
|
||||
foreach ($old_items as $delta => $info) {
|
||||
$old_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
|
||||
}
|
||||
foreach ($new_items as $delta => $info) {
|
||||
$new_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff field callback for parsing file field comparative values.
|
||||
*/
|
||||
function file_field_diff_view($items, $context) {
|
||||
$field = $context['field'];
|
||||
$instance = $context['instance'];
|
||||
$settings = $context['settings'];
|
||||
|
||||
$diff_items = array();
|
||||
foreach ($items as $delta => $item) {
|
||||
if (isset($item['file'])) {
|
||||
$output = array();
|
||||
|
||||
// We populate as much as possible to allow the best flexability in any
|
||||
// string overrides.
|
||||
$t_args = array();
|
||||
foreach ($item as $key => $value) {
|
||||
if (is_scalar($value)) {
|
||||
$t_args['!' . $key] = $value;
|
||||
}
|
||||
}
|
||||
// Some states do not have the file properties in the item, so put these
|
||||
// out of the main file object.
|
||||
if (!empty($item['file'])) {
|
||||
$file_values = (array) $item['file'];
|
||||
foreach ($file_values as $key => $value) {
|
||||
if (is_scalar($value) && !isset($t_args['!' . $key])) {
|
||||
$t_args['!' . $key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output['file'] = t('File: !filename', $t_args);
|
||||
if ($settings['compare_description_field'] && !empty($instance['settings']['description_field'])) {
|
||||
if (!empty($item['description'])) {
|
||||
$output['description'] = t('Description: !description', $t_args);
|
||||
}
|
||||
}
|
||||
if ($settings['show_id']) {
|
||||
$output['fid'] = t('File ID: !fid', $t_args);
|
||||
}
|
||||
if ($settings['compare_display_field'] && !empty($field['settings']['display_field'])) {
|
||||
$output['display'] = $item['display'] ? t('Displayed') : t('Hidden');
|
||||
}
|
||||
$diff_items[$delta] = implode('; ', $output);
|
||||
}
|
||||
}
|
||||
|
||||
return $diff_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide default field comparison options.
|
||||
*/
|
||||
function file_field_diff_default_options($field_type) {
|
||||
return array(
|
||||
'show_id' => 0,
|
||||
'compare_display_field' => 0,
|
||||
'compare_description_field' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a form for setting the field comparison options.
|
||||
*/
|
||||
function file_field_diff_options_form($field_type, $settings) {
|
||||
$options_form = array();
|
||||
$options_form['show_id'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show file ID'),
|
||||
'#default_value' => $settings['show_id'],
|
||||
);
|
||||
$options_form['compare_description_field'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Compare description field'),
|
||||
'#default_value' => $settings['compare_description_field'],
|
||||
'#description' => t('This is only used if the "Enable <em>Description</em> field" is checked in the instance settings.'),
|
||||
);
|
||||
$options_form['compare_display_field'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Compare display state field'),
|
||||
'#default_value' => $settings['compare_display_field'],
|
||||
'#description' => t('This is only used if the "Enable <em>Display</em> field" is checked in the field settings.'),
|
||||
);
|
||||
return $options_form;
|
||||
}
|
112
sites/all/modules/contrib/content/diff/includes/image.inc
Normal file
112
sites/all/modules/contrib/content/diff/includes/image.inc
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide diff field functions for the image module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Diff field callback for preloading the image file entities.
|
||||
*/
|
||||
function image_field_diff_view_prepare(&$old_items, &$new_items, $context) {
|
||||
$fids = array();
|
||||
foreach (array_merge_recursive($old_items, $new_items) as $info) {
|
||||
$fids[$info['fid']] = $info['fid'];
|
||||
}
|
||||
$files = file_load_multiple($fids);
|
||||
|
||||
foreach ($old_items as $delta => $info) {
|
||||
$old_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
|
||||
}
|
||||
foreach ($new_items as $delta => $info) {
|
||||
$new_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff field callback for parsing image field comparative values.
|
||||
*/
|
||||
function image_field_diff_view($items, $context) {
|
||||
$instance = $context['instance'];
|
||||
$settings = $context['settings'];
|
||||
|
||||
$diff_items = array();
|
||||
foreach ($items as $delta => $item) {
|
||||
if (isset($item['file'])) {
|
||||
$output = array();
|
||||
|
||||
// We populate as much as possible to allow the best flexability in any
|
||||
// string overrides.
|
||||
$t_args = array();
|
||||
foreach ($item as $key => $value) {
|
||||
if (is_scalar($value)) {
|
||||
$t_args['!' . $key] = $value;
|
||||
}
|
||||
}
|
||||
// Some states do not have the file properties in the item, so put these
|
||||
// out of the main file object.
|
||||
if (!empty($item['file'])) {
|
||||
$file_values = (array) $item['file'];
|
||||
foreach ($file_values as $key => $value) {
|
||||
if (is_scalar($value) && !isset($t_args['!' . $key])) {
|
||||
$t_args['!' . $key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output[] = t('Image: !filename', $t_args);
|
||||
if ($settings['compare_alt_field'] && !empty($instance['settings']['alt_field'])) {
|
||||
if (!empty($item['alt'])) {
|
||||
$output[] = t('Alt: !alt', $t_args);
|
||||
}
|
||||
}
|
||||
if ($settings['compare_title_field'] && !empty($instance['settings']['title_field'])) {
|
||||
if (!empty($item['title'])) {
|
||||
$output[] = t('Title: !title', $t_args);
|
||||
}
|
||||
}
|
||||
if ($settings['show_id']) {
|
||||
$output[] = t('File ID: !fid', $t_args);
|
||||
}
|
||||
$diff_items[$delta] = implode('; ', $output);
|
||||
}
|
||||
}
|
||||
|
||||
return $diff_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide default field comparison options.
|
||||
*/
|
||||
function image_field_diff_default_options($field_type) {
|
||||
return array(
|
||||
'show_id' => 0,
|
||||
'compare_alt_field' => 0,
|
||||
'compare_title_field' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a form for setting the field comparison options.
|
||||
*/
|
||||
function image_field_diff_options_form($field_type, $settings) {
|
||||
$options_form = array();
|
||||
$options_form['show_id'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show image ID'),
|
||||
'#default_value' => $settings['show_id'],
|
||||
);
|
||||
$options_form['compare_alt_field'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Compare <em>Alt</em> field'),
|
||||
'#default_value' => $settings['compare_alt_field'],
|
||||
'#description' => t('This is only used if the "Enable <em>Alt</em> field" is checked in the instance settings.'),
|
||||
);
|
||||
$options_form['compare_title_field'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Compare <em>Title</em> field'),
|
||||
'#default_value' => $settings['compare_title_field'],
|
||||
'#description' => t('This is only used if the "Enable <em>Title</em> field" is checked in the instance settings.'),
|
||||
);
|
||||
return $options_form;
|
||||
}
|
71
sites/all/modules/contrib/content/diff/includes/list.inc
Normal file
71
sites/all/modules/contrib/content/diff/includes/list.inc
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide diff field functions for the List module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Diff field callback for parsing list field comparative values.
|
||||
*/
|
||||
function list_field_diff_view($items, $context) {
|
||||
$field = $context['field'];
|
||||
$instance = $context['instance'];
|
||||
$settings = $context['settings'];
|
||||
|
||||
$diff_items = array();
|
||||
$allowed_values = list_allowed_values($field, $instance, $context['entity_type'], $context['entity']);
|
||||
foreach ($items as $delta => $item) {
|
||||
// Fairly complex condition to prevent duplicate "key (key)" type rendering.
|
||||
if (isset($allowed_values[$item['value']]) &&
|
||||
$allowed_values[$item['value']] != $item['value'] &&
|
||||
strlen($allowed_values[$item['value']])) {
|
||||
switch ($settings['compare']) {
|
||||
case 'both':
|
||||
$diff_items[$delta] = $allowed_values[$item['value']] . ' (' . $item['value'] . ')';
|
||||
break;
|
||||
|
||||
case 'label':
|
||||
$diff_items[$delta] = $allowed_values[$item['value']];
|
||||
break;
|
||||
|
||||
default:
|
||||
$diff_items[$delta] = $item['value'];
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If no match was found for the label, fall back to the key.
|
||||
$diff_items[$delta] = $item['value'];
|
||||
}
|
||||
}
|
||||
return $diff_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide default field comparison options.
|
||||
*/
|
||||
function list_field_diff_default_options($field_type) {
|
||||
return array(
|
||||
'compare' => 'label',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a form for setting the field comparison options.
|
||||
*/
|
||||
function list_field_diff_options_form($field_type, $settings) {
|
||||
$options_form = array();
|
||||
$options_form['compare'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Comparison method'),
|
||||
'#options' => array(
|
||||
'label' => t('Label'),
|
||||
'key' => t('Key'),
|
||||
'both' => t('Label (key)'),
|
||||
),
|
||||
'#default_value' => $settings['compare'],
|
||||
);
|
||||
return $options_form;
|
||||
}
|
107
sites/all/modules/contrib/content/diff/includes/node.inc
Normal file
107
sites/all/modules/contrib/content/diff/includes/node.inc
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide diff functions for the node module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_diff().
|
||||
*
|
||||
* This function compares core node properties. This is currently limited to:
|
||||
* - title: The title of the node.
|
||||
*
|
||||
* @param object $old_node
|
||||
* The older node revision.
|
||||
* @param object $new_node
|
||||
* The newer node revision.
|
||||
* @param array $context
|
||||
* An associative array containing:
|
||||
* - entity_type: The entity type; e.g., 'node' or 'user'.
|
||||
* - old_entity: The older entity.
|
||||
* - new_entity: The newer entity.
|
||||
* - view_mode: The view mode to use. Defaults to FALSE. If no view mode is
|
||||
* given, the recommended fallback view mode is 'default'.
|
||||
* - states: An array of view states. These could be one of:
|
||||
* - raw: The raw value of the diff, the classic 7.x-2.x view.
|
||||
* - rendered: The rendered HTML as determined by the view mode. Only
|
||||
* return markup for this state if the value is normally shown
|
||||
* by this view mode. The user will most likely be able to see
|
||||
* the raw or raw_plain state, so this is optional.
|
||||
*
|
||||
* The rendering state is a work in progress.
|
||||
*
|
||||
* Conditionally, you can get these states, but setting these will override
|
||||
* the user selectable markdown method.
|
||||
*
|
||||
* - raw_plain: As raw, but text should be markdowned.
|
||||
* - rendered_plain: As rendered, but text should be markdowned.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of values keyed by the entity property.
|
||||
*
|
||||
* This is effectively an unnested Form API-like structure.
|
||||
*
|
||||
* States are returned as follows:
|
||||
*
|
||||
* $results['line'] = array(
|
||||
* '#name' => t('Line'),
|
||||
* '#states' => array(
|
||||
* 'raw' => array(
|
||||
* '#old' => '<p class="line">This was the old line number [tag].</p>',
|
||||
* '#new' => '<p class="line">This is the new line [tag].</p>',
|
||||
* ),
|
||||
* 'rendered' => array(
|
||||
* '#old' => '<p class="line">This was the old line number <span class="line-number">57</span>.</p>',
|
||||
* '#new' => '<p class="line">This is the new line <span class="line-number">57</span>.</p>',
|
||||
* ),
|
||||
* ),
|
||||
* );
|
||||
*
|
||||
* For backwards compatability, no changes are required to support states,
|
||||
* but it is recommended to provide a better UI for end users.
|
||||
*
|
||||
* For example, the following example is equivalent to returning the raw
|
||||
* state from the example above.
|
||||
*
|
||||
* $results['line'] = array(
|
||||
* '#name' => t('Line'),
|
||||
* '#old' => '<p class="line">This was the old line number [tag].</p>',
|
||||
* '#new' => '<p class="line">This is the new line [tag].</p>',
|
||||
* );
|
||||
*/
|
||||
function node_entity_diff($old_node, $new_node, $context) {
|
||||
$result = array();
|
||||
if ($context['entity_type'] == 'node') {
|
||||
$type = node_type_get_type($new_node);
|
||||
$result['title'] = array(
|
||||
'#name' => $type->title_label,
|
||||
'#states' => array(),
|
||||
'#weight' => -5,
|
||||
'#settings' => array(
|
||||
// Global setting - 'diff_show_header_' . $entity_type
|
||||
'show_header' => variable_get('diff_show_header_node', 1),
|
||||
),
|
||||
);
|
||||
foreach ($context['states'] as $state) {
|
||||
switch ($state) {
|
||||
case 'rendered':
|
||||
$result['title']['#states'][$state] = array(
|
||||
'#old' => l($old_node->title, 'node/' . $old_node->title),
|
||||
'#new' => l($new_node->title, 'node/' . $new_node->title),
|
||||
);
|
||||
break;
|
||||
|
||||
// We specify a default so that the title is allows compared.
|
||||
case 'raw':
|
||||
default:
|
||||
$result['title']['#states'][$state] = array(
|
||||
'#old' => array($old_node->title),
|
||||
'#new' => array($new_node->title),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
17
sites/all/modules/contrib/content/diff/includes/number.inc
Normal file
17
sites/all/modules/contrib/content/diff/includes/number.inc
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide diff field functions for the Number module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Diff field callback for parsing number field comparative values.
|
||||
*/
|
||||
function number_field_diff_view($items, $context) {
|
||||
$diff_items = array();
|
||||
foreach ($items as $delta => $item) {
|
||||
$diff_items[$delta] = $item['value'];
|
||||
}
|
||||
return $diff_items;
|
||||
}
|
107
sites/all/modules/contrib/content/diff/includes/taxonomy.inc
Normal file
107
sites/all/modules/contrib/content/diff/includes/taxonomy.inc
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Implements pusdeo-hook hook_field_diff_view() for the Taxonomy module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Diff field callback for preloading term entities.
|
||||
*/
|
||||
function taxonomy_field_diff_view_prepare(&$old_items, &$new_items, $context) {
|
||||
$tids = array();
|
||||
foreach (array_merge_recursive($old_items, $new_items) as $info) {
|
||||
$tids[$info['tid']] = $info['tid'];
|
||||
}
|
||||
$terms = taxonomy_term_load_multiple($tids);
|
||||
foreach ($old_items as $delta => $info) {
|
||||
$old_items[$delta]['term'] = isset($terms[$info['tid']]) ? $terms[$info['tid']] : NULL;
|
||||
}
|
||||
foreach ($new_items as $delta => $info) {
|
||||
$new_items[$delta]['term'] = isset($terms[$info['tid']]) ? $terms[$info['tid']] : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff field callback for parsing term field comparative values.
|
||||
*/
|
||||
function taxonomy_field_diff_view($items, $context) {
|
||||
$settings = $context['settings'];
|
||||
$instance = $context['instance'];
|
||||
|
||||
$diff_items = array();
|
||||
foreach ($items as $delta => $item) {
|
||||
if (!empty($item['tid'])) {
|
||||
if ($item['tid'] == 'autocreate') {
|
||||
$diff_items[$delta] = t('!term_name (new)', array('!term_name' => $item['name']));
|
||||
}
|
||||
elseif (empty($item['term'])) {
|
||||
$diff_items[$delta] = t('Missing term reference (!tid)', array('!tid' => $item['tid']));
|
||||
}
|
||||
else {
|
||||
$output = array();
|
||||
$output['name'] = $item['term']->name;
|
||||
if ($settings['show_id']) {
|
||||
$output['tid'] = t('Term ID: !tid', array('!tid' => $item['term']->tid));
|
||||
}
|
||||
$diff_items[$delta] = implode('; ', $output);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($settings['sort']) && !empty($diff_items)) {
|
||||
if ($settings['sort'] == DIFF_SORT_VALUE || $instance['widget']['type'] == 'taxonomy_autocomplete') {
|
||||
usort($diff_items, 'uasort_taxonomy_field_diff_terms');
|
||||
}
|
||||
}
|
||||
return $diff_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for sorting terms.
|
||||
*/
|
||||
function uasort_taxonomy_field_diff_terms($a, $b) {
|
||||
// We need to use t() to test for string overrides.
|
||||
$missing_text = t('Missing term reference');
|
||||
$a_missing = strpos($a, $missing_text) === 0;
|
||||
$b_missing = strpos($b, $missing_text) === 0;
|
||||
if ($a_missing && $b_missing) {
|
||||
return strnatcmp($a, $b);
|
||||
}
|
||||
elseif ($a_missing xor $b_missing) {
|
||||
return $a_missing ? 100 : -100;
|
||||
}
|
||||
return strnatcmp($a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide default field comparison options.
|
||||
*/
|
||||
function taxonomy_field_diff_default_options($field_type) {
|
||||
return array(
|
||||
'show_id' => 0,
|
||||
'sort' => DIFF_SORT_CUSTOM,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a form for setting the field comparison options.
|
||||
*/
|
||||
function taxonomy_field_diff_options_form($field_type, $settings) {
|
||||
$options_form = array();
|
||||
$options_form['show_id'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show term ID'),
|
||||
'#default_value' => $settings['show_id'],
|
||||
);
|
||||
$options_form['sort'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Sort'),
|
||||
'#options' => array(
|
||||
DIFF_SORT_NONE => t('No sort'),
|
||||
DIFF_SORT_VALUE => t('Sort'),
|
||||
DIFF_SORT_CUSTOM => t('Sort if free tagging field'),
|
||||
),
|
||||
'#default_value' => $settings['sort'],
|
||||
);
|
||||
return $options_form;
|
||||
}
|
113
sites/all/modules/contrib/content/diff/includes/text.inc
Normal file
113
sites/all/modules/contrib/content/diff/includes/text.inc
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide diff field functions for the Text module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Diff field callback for parsing text field comparative values.
|
||||
*/
|
||||
function text_field_diff_view($items, $context) {
|
||||
$field = $context['field'];
|
||||
$instance = $context['instance'];
|
||||
$settings = $context['settings'];
|
||||
|
||||
$diff_items = array();
|
||||
foreach ($items as $delta => $item) {
|
||||
$diff_items[$delta] = array();
|
||||
|
||||
// Compute the format for appending to the label.
|
||||
$format_text = '';
|
||||
if ($instance['settings']['text_processing'] && $settings['compare_format']) {
|
||||
$format_id = empty($item['format']) ? filter_fallback_format() : $item['format'];
|
||||
if ($format = filter_format_load($format_id)) {
|
||||
$format_text = $format->name;
|
||||
}
|
||||
else {
|
||||
$format_text = t('Missing format !format', array('!format' => $format_id));
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the summary fields.
|
||||
$summary = $field['type'] == 'text_with_summary' && $settings['compare_summary'];
|
||||
if ($summary) {
|
||||
// Allow users to optionally clean system specific characters.
|
||||
if (empty($item['summary'])) {
|
||||
$diff_items[$delta][] = t('Summary field is empty.');
|
||||
}
|
||||
else {
|
||||
if ($format_text) {
|
||||
$diff_items[$delta][] = t('Summary (!text_format):', array('!text_format' => $format_text));
|
||||
}
|
||||
else {
|
||||
$diff_items[$delta][] = t('Summary:');
|
||||
}
|
||||
$diff_items[$delta][] = diff_normalise_text($item['summary']);
|
||||
}
|
||||
}
|
||||
|
||||
// Only show label if field has summary displayed.
|
||||
if ($summary) {
|
||||
if ($format_text) {
|
||||
$diff_items[$delta][] = t('Content (!text_format):', array('!text_format' => $format_text));
|
||||
}
|
||||
else {
|
||||
$diff_items[$delta][] = t('Content:');
|
||||
}
|
||||
}
|
||||
|
||||
// Allow users to optionally clean system specific characters.
|
||||
$diff_items[$delta][] = diff_normalise_text($item['value']);
|
||||
|
||||
// If no summary, append the format selection to the bottom of the screen.
|
||||
// This prevents adding the "Content (format)" label.
|
||||
if ($format_text && !$summary) {
|
||||
$diff_items[$delta][] = t('Text format: !text_format', array('!text_format' => $format_text));
|
||||
}
|
||||
|
||||
$diff_items[$delta] = $diff_items[$delta];
|
||||
}
|
||||
return $diff_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide default field comparison options.
|
||||
*/
|
||||
function text_field_diff_default_options($field_type) {
|
||||
// Overrides the global 'markdown' setting which does not escape HTML.
|
||||
$settings = array(
|
||||
'compare_format' => 0,
|
||||
'markdown' => 'drupal_html_to_text',
|
||||
'line_counter' => '',
|
||||
);
|
||||
if ($field_type == 'text_with_summary') {
|
||||
$settings += array(
|
||||
'compare_summary' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a form for setting the field comparison options.
|
||||
*/
|
||||
function text_field_diff_options_form($field_type, $settings) {
|
||||
$options_form = array();
|
||||
$options_form['compare_format'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Compare format'),
|
||||
'#default_value' => $settings['compare_format'],
|
||||
'#description' => t('This is only used if the "Text processing" instance settings are set to <em>Filtered text (user selects text format)</em>.'),
|
||||
);
|
||||
if ($field_type == 'text_with_summary') {
|
||||
$options_form['compare_summary'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Compare summary separately'),
|
||||
'#default_value' => $settings['compare_summary'],
|
||||
'#description' => t('This is only used if the "Summary input" option is checked in the instance settings.'),
|
||||
);
|
||||
}
|
||||
return $options_form;
|
||||
}
|
58
sites/all/modules/contrib/content/diff/js/diff.js
Normal file
58
sites/all/modules/contrib/content/diff/js/diff.js
Normal file
@@ -0,0 +1,58 @@
|
||||
(function ($) {
|
||||
|
||||
Drupal.behaviors.diffRevisions = {
|
||||
attach: function (context, settings) {
|
||||
var $rows = $('table.diff-revisions tbody tr');
|
||||
function updateDiffRadios() {
|
||||
var newTd = false;
|
||||
var oldTd = false;
|
||||
if (!$rows.length) {
|
||||
return true;
|
||||
}
|
||||
$rows.removeClass('selected').each(function() {
|
||||
var $row = $(this);
|
||||
$row.removeClass('selected');
|
||||
var $inputs = $row.find('input[type="radio"]');
|
||||
var $oldRadio = $inputs.filter('[name="old"]').eq(0);
|
||||
var $newRadio = $inputs.filter('[name="new"]').eq(0);
|
||||
if (!$oldRadio.length || !$newRadio.length) {
|
||||
return true;
|
||||
}
|
||||
if ($oldRadio.attr('checked')) {
|
||||
oldTd = true;
|
||||
$row.addClass('selected');
|
||||
$oldRadio.css('visibility', 'visible');
|
||||
$newRadio.css('visibility', 'hidden');
|
||||
} else if ($newRadio.attr('checked')) {
|
||||
newTd = true;
|
||||
$row.addClass('selected');
|
||||
$oldRadio.css('visibility', 'hidden');
|
||||
$newRadio.css('visibility', 'visible');
|
||||
} else {
|
||||
if (Drupal.settings.diffRevisionRadios == 'linear') {
|
||||
if (newTd && oldTd) {
|
||||
$oldRadio.css('visibility', 'visible');
|
||||
$newRadio.css('visibility', 'hidden');
|
||||
} else if (newTd) {
|
||||
$newRadio.css('visibility', 'visible');
|
||||
$oldRadio.css('visibility', 'visible');
|
||||
} else {
|
||||
$newRadio.css('visibility', 'visible');
|
||||
$oldRadio.css('visibility', 'hidden');
|
||||
}
|
||||
} else {
|
||||
$newRadio.css('visibility', 'visible');
|
||||
$oldRadio.css('visibility', 'visible');
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (Drupal.settings.diffRevisionRadios) {
|
||||
$rows.find('input[name="new"], input[name="old"]').click(updateDiffRadios);
|
||||
updateDiffRadios();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
136
sites/all/modules/contrib/content/diff/readme.txt
Normal file
136
sites/all/modules/contrib/content/diff/readme.txt
Normal file
@@ -0,0 +1,136 @@
|
||||
Diff module - http://drupal.org/project/diff
|
||||
============================================
|
||||
|
||||
Diff enhances usage of node revisions by adding the following features:
|
||||
|
||||
- Diff between node revisions on the 'Revisions' tab to view all the changes
|
||||
between any two revisions of a node.
|
||||
- Highlight changes inline while viewing a node to quickly see color-coded
|
||||
additions, changes, and deletions.
|
||||
- Preview changes as a diff before updating a node.
|
||||
|
||||
It is also an API to compare any entities although this functionality is not
|
||||
exposed by the core Diff module.
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
Drupal 7.x
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
1. Place the Diff module into your modules directory.
|
||||
This is normally the "sites/all/modules" directory.
|
||||
|
||||
2. Go to admin/build/modules. Enable the module.
|
||||
The Diff modules is found in the Other section.
|
||||
|
||||
Read more about installing modules at http://drupal.org/node/70151
|
||||
|
||||
See the configuration section below.
|
||||
|
||||
UPGRADING
|
||||
---------
|
||||
Any updates should be automatic. Just remember to run update.php!
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
Unlike the earlier version, the module now has a lot of configurable settings.
|
||||
|
||||
Global settings can be found under Configuration > Content > Diff
|
||||
|
||||
i.e. http://www.example.com/admin/config/content/diff
|
||||
|
||||
Entity specific settings would be listed under the entities settings. This
|
||||
module only handles Node revisioning functionality, and these are detailed
|
||||
below.
|
||||
|
||||
1) Node revisioning settings
|
||||
|
||||
Diff needs to be configured to be used with specific node types on your site.
|
||||
To enable any of Diff's options on a content type's settings page.
|
||||
|
||||
e.g. http://www.example.com/admin/structure/types/manage/page
|
||||
|
||||
a) Diff options
|
||||
|
||||
Under "Compare revisions", enable the settings that you want;
|
||||
|
||||
i) "Show View changes button on node edit form" adds a new "Preview" like
|
||||
submit button to node editing pages. This shows a diff preview.
|
||||
|
||||
ii) "Enable the Revisions page for this content type" adds the revisioning
|
||||
tab to content. This allows users to compare between various revisions
|
||||
that they have access to.
|
||||
|
||||
iii) "Standard comparison preview" option allows you to control how the most
|
||||
current revision is show on the revision comparision page.
|
||||
|
||||
b) Publishing options
|
||||
|
||||
It is strongly advised that you also enable the automatic creation of
|
||||
revisions on any content types you want to use this with. If you do not do
|
||||
this, chances are there will be limited revisioning information available to
|
||||
compare.
|
||||
|
||||
Under "Publishing options", enable "Create new revision".
|
||||
|
||||
2) Field revisioning settings
|
||||
|
||||
Global settings per field type can be found here:
|
||||
|
||||
http://www.example.com/admin/config/content/diff/fields
|
||||
|
||||
"Show field title" toggles field title visibility on the comparison page.
|
||||
|
||||
"Markdown callback" is the callback used to render the field when viewing the
|
||||
page in the "Marked down" page view.
|
||||
|
||||
"Line counter" is an optional. This shows the approximate line number where
|
||||
the change occurred. This is an approximate counter only.
|
||||
|
||||
Other fields add additional settings here.
|
||||
|
||||
3) Entity revisioning settings
|
||||
|
||||
Global configurable settings limited to node entities.
|
||||
|
||||
a) Show entity label header
|
||||
|
||||
This provides a field like title for the entity label field.
|
||||
|
||||
i.e. For nodes, this provides a header for the node's title.
|
||||
|
||||
b) Treat diff pages as administrative
|
||||
|
||||
By default, the revisioning pages are administrative, i.e. they will use the
|
||||
administration theme. You can block this by unchecking this option.
|
||||
|
||||
4) Global settings
|
||||
|
||||
A small number of new features have been added to the 7.x-3.x branch, these
|
||||
include the ability to change the leading and trailing lines in the comparison,
|
||||
a new CSS theme for the diff pages, new JScript options for the revisioning
|
||||
selection form and options to help prevent cross operating systems in relation
|
||||
to line endings.
|
||||
|
||||
http://www.example.com/admin/config/content/diff
|
||||
|
||||
Technical
|
||||
---------
|
||||
- Diff compares the raw data, not the filtered output, making it easier to see
|
||||
changes to HTML entities, etc.
|
||||
- The diff engine itself is a GPL'ed php diff engine from phpwiki.
|
||||
|
||||
API
|
||||
---
|
||||
See diff.api.php
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
- realityloop (Brian Gilbert)
|
||||
- Alan D. (Alan Davison)
|
||||
- dww (Derek Wright)
|
||||
- moshe (Moshe Weitzman)
|
||||
- rötzi (Julian)
|
||||
- yhahn (Young Hahn)
|
339
sites/all/modules/contrib/content/node_clone/LICENSE.txt
Normal file
339
sites/all/modules/contrib/content/node_clone/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.
|
51
sites/all/modules/contrib/content/node_clone/README.txt
Normal file
51
sites/all/modules/contrib/content/node_clone/README.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
README file for the clone module for Drupal 7.x.
|
||||
|
||||
The clone module allows users to make a copy of an existing node and then edit
|
||||
that copy. The authorship is set to the current user, the menu and url aliases
|
||||
are reset, and the (localized) words "Clone of" are inserted into the title to
|
||||
remind you that you are not editing the original node.
|
||||
|
||||
Users with the "clone node" or "clone own nodes" permission can utilize this
|
||||
functionality. A "Clone content" local action link will appear on node pages.
|
||||
Depending on the setting you choose there are two possible actions that will
|
||||
occur when you click that tab. Each of these is a different cloning "method".
|
||||
|
||||
The default method works by pre-populating the node form, rather than immediately
|
||||
saving a copy of the original node to the database. Thus, your node will not
|
||||
be saved until you hit "Submit" (just like if you went to node/add/x).
|
||||
|
||||
The alternative method that may be selected immediately saves the node (by
|
||||
default the user must confirm this action on a confirmation form). This may
|
||||
work better in some cases, but also means that the copied node may immediately
|
||||
show up on the front page or in RSS feeds even while you are editing it.
|
||||
|
||||
This module makes reasonable checks on access permissions. A user cannot clone
|
||||
a node unless they can use the input format of that node, and unless they have
|
||||
permission to create new nodes of that type based on a call to node_access().
|
||||
|
||||
Settings can be accessed at admin/config/content/clone. On this page you can
|
||||
set whether the publishing options are reset when making a clone of a node.
|
||||
This is set for each node type individually.
|
||||
|
||||
This module seems to work with common node types, however YMMV, especially with
|
||||
nodes that have any sort of image or file attachments. In all cases, but
|
||||
especially if you are using a complex (non-core) field or custom node type,
|
||||
you should evaluate this module on a test site with a copy of your database
|
||||
before attempting to use it on a live site. On the settings page you may choose
|
||||
node types to be omitted from the cloning mechanism - no users will be able
|
||||
to clone a node of an omitted type.
|
||||
|
||||
This module makes no attempt to respect field-level permissions set via
|
||||
the Field Permissions module, or any other module that implements
|
||||
field-level permissions hooks. You should test your configuration
|
||||
carefully or not allow access to the clone functionality for non-admins if
|
||||
you rely on field-level permissions for important site features.
|
||||
|
||||
To install this module, copy the folder with all the files to the
|
||||
/sites/all/modules OR /sites/default/modules directory of your Drupal
|
||||
installation and enable it at /admin/build/modules. Two new permissions are
|
||||
available, but there are no changes to the database structure.
|
||||
|
||||
Note: this module originally derived from code posted by Steve Ringwood
|
||||
(nevets@drupal) at http://drupal.org/node/73381#comment-137714
|
47
sites/all/modules/contrib/content/node_clone/clone.api.php
Normal file
47
sites/all/modules/contrib/content/node_clone/clone.api.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API documentation for the Node clone module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter the node before saving a clone.
|
||||
*
|
||||
* @param $node
|
||||
* Reference to the fully loaded node object being saved (the clone) that
|
||||
* can be altered as needed.
|
||||
* @param array $context
|
||||
* An array of context describing the clone operation. The keys are:
|
||||
* - 'method' : Can be either 'prepopulate' or 'save-edit'.
|
||||
* - 'original_node' : The original fully loaded node object being cloned.
|
||||
*
|
||||
* @see clone_node_save()
|
||||
* @see drupal_alter()
|
||||
*/
|
||||
function hook_clone_node_alter(&$node, $context) {
|
||||
if ($context['original_node']->type == 'special') {
|
||||
$node->special = special_something();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the access to the ability to clone a given node.
|
||||
*
|
||||
* @param bool $access
|
||||
* Reference to the boolean determining if cloning should be allowed on a
|
||||
* given node.
|
||||
* @param $node
|
||||
* The fully loaded node object being considered for cloning.
|
||||
*
|
||||
* @see clone_access_cloning()
|
||||
* @see drupal_alter()
|
||||
*/
|
||||
function hook_clone_access_alter(&$access, $node) {
|
||||
global $user;
|
||||
// Only allow cloning of nodes posted to groups you belong to.
|
||||
// This function doesn't really exist, but you get the idea...
|
||||
if (!og_user_is_member_of_group_the_node_is_in($user, $node)) {
|
||||
$access = FALSE;
|
||||
}
|
||||
}
|
13
sites/all/modules/contrib/content/node_clone/clone.info
Normal file
13
sites/all/modules/contrib/content/node_clone/clone.info
Normal file
@@ -0,0 +1,13 @@
|
||||
name = "Node clone"
|
||||
description = "Allows users to clone (copy then edit) an existing node."
|
||||
core = 7.x
|
||||
|
||||
files[] = views/views_handler_field_node_link_clone.inc
|
||||
configure = admin/config/content/clone
|
||||
|
||||
; Information added by Drupal.org packaging script on 2014-01-04
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "node_clone"
|
||||
datestamp = "1388860408"
|
||||
|
21
sites/all/modules/contrib/content/node_clone/clone.install
Normal file
21
sites/all/modules/contrib/content/node_clone/clone.install
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Install & uninstall functions for Node_Clone module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of hook_uninstall.
|
||||
*/
|
||||
function clone_uninstall() {
|
||||
variable_del('clone_method');
|
||||
variable_del('clone_omitted');
|
||||
variable_del('clone_nodes_without_confirm');
|
||||
variable_del('clone_use_node_type_name');
|
||||
variable_del('clone_menu_links');
|
||||
$types = node_type_get_names();
|
||||
foreach ($types as $type => $name) {
|
||||
variable_del('clone_reset_' . $type);
|
||||
}
|
||||
}
|
||||
|
284
sites/all/modules/contrib/content/node_clone/clone.module
Normal file
284
sites/all/modules/contrib/content/node_clone/clone.module
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Allow users to make a copy of an item of content (a node) and then edit that copy.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of hook_help().
|
||||
*/
|
||||
function clone_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/help#clone':
|
||||
$output = '<p>' . t('The clone module allows users to make a copy of an existing node and then edit that copy. The authorship is set to the current user, the menu and url aliases are reset, and the words "Clone of" are inserted into the title to remind you that you are not editing the original node.') . '</p>';
|
||||
$output .= '<p>' . t('Users with the "clone node" permission can utilize this functionality. A new tab will appear on node pages with the word "Clone".') . '</p>';
|
||||
return $output;
|
||||
case 'node/%/clone':
|
||||
$method = variable_get('clone_method', 'prepopulate');
|
||||
if ($method == 'prepopulate') {
|
||||
return t('This clone will not be saved to the database until you submit.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_permission().
|
||||
*/
|
||||
function clone_permission() {
|
||||
return array(
|
||||
'clone node' => array('title' => t('Clone any node')),
|
||||
'clone own nodes' => array('title' => t('Clone own nodes.')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_menu().
|
||||
*/
|
||||
function clone_menu() {
|
||||
$items['admin/config/content/clone'] = array(
|
||||
'access arguments' => array('administer site configuration'),
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('clone_settings'),
|
||||
'title' => 'Node clone module',
|
||||
'file' => 'clone.pages.inc',
|
||||
'description' => 'Allows users to clone (copy then edit) an existing node.',
|
||||
);
|
||||
$items['node/%node/clone/%clone_token'] = array(
|
||||
'access callback' => 'clone_access_cloning',
|
||||
'access arguments' => array(1, TRUE, 3),
|
||||
'page callback' => 'clone_node_check',
|
||||
'page arguments' => array(1),
|
||||
'title' => 'Clone content',
|
||||
'title callback' => 'clone_action_link_title',
|
||||
'title arguments' => array(1),
|
||||
'weight' => 5,
|
||||
'file' => 'clone.pages.inc',
|
||||
'type' => MENU_LOCAL_ACTION,
|
||||
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
function clone_token_to_arg($arg = NULL, $map = NULL, $index = NULL) {
|
||||
// Supply CSRF token if needed.
|
||||
if (variable_get('clone_nodes_without_confirm', FALSE)) {
|
||||
return drupal_get_token('clone_access_cloning');
|
||||
}
|
||||
return 'confirm';
|
||||
}
|
||||
|
||||
function clone_access_cloning($node, $check_token = FALSE, $token = FALSE) {
|
||||
global $user;
|
||||
// Check CSRF token if needed.
|
||||
if ($check_token) {
|
||||
if (!$token || $token !== clone_token_to_arg()) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
// Check basic permissions first.
|
||||
$access = clone_is_permitted($node->type) && (user_access('clone node') || ($user->uid && ($node->uid == $user->uid) && user_access('clone own nodes')));
|
||||
// Make sure the user can view the original node content, and create a new one..
|
||||
$access = $access && node_access('view', $node) && node_access('create', $node->type);
|
||||
// Let other modules alter this.
|
||||
drupal_alter("clone_access", $access, $node);
|
||||
return $access;
|
||||
}
|
||||
|
||||
function clone_is_permitted($type) {
|
||||
$omitted = variable_get('clone_omitted', array());
|
||||
return empty($omitted[$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu title callback.
|
||||
*/
|
||||
function clone_action_link_title($node) {
|
||||
// A hack to present a shorter title in contextual links.
|
||||
if (current_path() != 'node/' . $node->nid) {
|
||||
return t('Clone');
|
||||
}
|
||||
if (variable_get('clone_use_node_type_name', 0)) {
|
||||
return t('Clone this !type', array('!type' => drupal_strtolower(node_type_get_name($node))));
|
||||
}
|
||||
return t('Clone content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_node_type_delete().
|
||||
*/
|
||||
function clone_node_type_delete($info) {
|
||||
variable_del('clone_reset_' . $info->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_node_type_update().
|
||||
*/
|
||||
function clone_node_type_update($info) {
|
||||
if (!empty($info->old_type) && $info->old_type != $info->type) {
|
||||
if (variable_get('clone_reset_' . $info->old_type, FALSE)) {
|
||||
variable_del('clone_reset_' . $info->old_type);
|
||||
variable_set('clone_reset_' . $info->type, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_api.
|
||||
*/
|
||||
function clone_views_api() {
|
||||
return array(
|
||||
'api' => 3,
|
||||
'path' => drupal_get_path('module', 'clone') . '/views',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_admin_paths().
|
||||
*/
|
||||
function clone_admin_paths() {
|
||||
if (variable_get('node_admin_theme')) {
|
||||
$paths = array(
|
||||
'node/*/clone/*' => TRUE,
|
||||
);
|
||||
return $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter().
|
||||
*/
|
||||
function clone_form_node_form_alter(&$form, $form_state, $form_id) {
|
||||
// Add the clone_from_original_nid value for node forms triggered by cloning.
|
||||
// This will make sure the clone_from_original_nid property is still
|
||||
// attached to the node when passing through hook_node_insert().
|
||||
if (!empty($form['#node']->clone_from_original_nid)) {
|
||||
$form['clone_from_original_nid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $form['#node']->clone_from_original_nid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
function clone_form_node_admin_content_alter(&$form, $form_state, $form_id) {
|
||||
$destination = drupal_get_destination();
|
||||
// The property attribute changes in the $form array depending on the user role.
|
||||
$property = isset($form['admin']['nodes']['#options']) ? '#options' : '#rows';
|
||||
if (empty($form['admin']['nodes'][$property])) {
|
||||
return;
|
||||
}
|
||||
// Expose a Clone operation on each node.
|
||||
foreach ($form['admin']['nodes'][$property] as $nid => &$row) {
|
||||
$node = node_load($nid);
|
||||
if (clone_access_cloning($node)) {
|
||||
$row['operations']['data']['#links']['clone'] = array(
|
||||
'title' => t('clone'),
|
||||
'href' => 'node/' . $nid . '/clone/' . clone_token_to_arg(),
|
||||
'query' => $destination,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function clone_action_info() {
|
||||
return array(
|
||||
'clone_action_clone' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Clone item'),
|
||||
'configurable' => TRUE,
|
||||
'hooks' => array('any' => TRUE),
|
||||
'triggers' => array('any'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action callback.
|
||||
*/
|
||||
function clone_action_clone($original_node, $context) {
|
||||
module_load_include('inc', 'clone', 'clone.pages');
|
||||
if (clone_is_permitted($original_node->type)) {
|
||||
$val = !empty($context['clone_context']) ? $context['clone_context'] : array();
|
||||
$node = _clone_node_prepare($original_node, !empty($val['prefix_title']));
|
||||
if (isset($val['substitute_from']) && strlen($val['substitute_from']) && isset($val['substitute_to'])) {
|
||||
$i = (!empty($val['substitute_case_insensitive']) ? 'i' : '');
|
||||
$pattern = '#'. strtr($val['substitute_from'], array('#' => '\#')) . '#' . $i;
|
||||
foreach (array('title') as $property) {
|
||||
$new = preg_replace($pattern, $val['substitute_to'], $node->{$property});
|
||||
if ($new) {
|
||||
$node->{$property} = $new;
|
||||
}
|
||||
}
|
||||
foreach (array('body') as $property) {
|
||||
foreach($node->{$property} as $lang => $row) {
|
||||
foreach ($row as $delta => $data) {
|
||||
foreach (array('value', 'summary') as $key) {
|
||||
if (isset($node->{$property}[$lang][$delta][$key])) {
|
||||
$new = preg_replace($pattern, $val['substitute_to'], $node->{$property}[$lang][$delta][$key]);
|
||||
if ($new) {
|
||||
$node->{$property}[$lang][$delta][$key] = $new;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Let other modules do special fixing up.
|
||||
$context = array('method' => 'action', 'original_node' => $original_node);
|
||||
drupal_alter('clone_node', $node, $context);
|
||||
node_save($node);
|
||||
if (module_exists('rules')) {
|
||||
rules_invoke_event('clone_node', $node, $original_node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Clone failed for %title : not permitted for nodes of type %type', array('%title' => $original_node->title, '%type' => $original_node->type)), 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action form.
|
||||
*/
|
||||
function clone_action_clone_form($context) {
|
||||
$form['clone_context'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
$form['clone_context']['prefix_title'] = array(
|
||||
'#title' => t('Prefix title'),
|
||||
'#type' => 'checkbox',
|
||||
'#description' => t('Should cloned node tiles be prefixed?'),
|
||||
'#default_value' => isset($context['clone_context']['prefix_title']) ? $context['clone_context']['prefix_title'] : 1,
|
||||
);
|
||||
$form['clone_context']['substitute_from'] = array(
|
||||
'#title' => t('Substitute from string'),
|
||||
'#type' => 'textfield',
|
||||
'#description' => t('String (or regex) to substitute from in title and body.'),
|
||||
'#default_value' => isset($context['clone_context']['substitue_from']) ? $context['clone_context']['substitue_from'] : '',
|
||||
);
|
||||
$form['clone_context']['substitute_to'] = array(
|
||||
'#title' => t('Substitute to string'),
|
||||
'#type' => 'textfield',
|
||||
'#description' => t('String (or regex) to substitute to in title and body.'),
|
||||
'#default_value' => isset($context['clone_context']['substitue_to']) ? $context['clone_context']['substitue_to'] : '',
|
||||
);
|
||||
$form['clone_context']['substitute_case_insensitive'] = array(
|
||||
'#title' => t('Case insensitive substitution'),
|
||||
'#type' => 'checkbox',
|
||||
'#description' => t('Should the substituion match be case insensitive?'),
|
||||
'#default_value' => isset($context['clone_context']['substitute_case_insensitive']) ? $context['clone_context']['substitute_case_insensitive'] : NULL,
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action form submit.
|
||||
*/
|
||||
function clone_action_clone_submit($form, $form_state) {
|
||||
return array('clone_context' => $form_state['values']['clone_context']);
|
||||
}
|
237
sites/all/modules/contrib/content/node_clone/clone.pages.inc
Normal file
237
sites/all/modules/contrib/content/node_clone/clone.pages.inc
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Additional functions for Node_Clone module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback to configure module settings.
|
||||
*/
|
||||
function clone_settings($form, &$form_state) {
|
||||
|
||||
$form['basic'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('General settings'),
|
||||
);
|
||||
$form['basic']['clone_method'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Method to use when cloning a node'),
|
||||
'#options' => array('prepopulate' => t('Pre-populate the node form fields'), 'save-edit' => t('Save as a new node then edit')),
|
||||
'#default_value' => variable_get('clone_method', 'prepopulate'),
|
||||
);
|
||||
$form['basic']['clone_nodes_without_confirm'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Confirmation mode when using the "Save as a new node then edit" method'),
|
||||
'#default_value' => (int)variable_get('clone_nodes_without_confirm', 0),
|
||||
'#options' => array(t('Require confirmation (recommended)'), t('Bypass confirmation')),
|
||||
'#description' => t('A new node may be saved immediately upon clicking the "clone" link when viewing a node, bypassing the normal confirmation form.'),
|
||||
);
|
||||
$form['basic']['clone_menu_links'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Clone menu links'),
|
||||
'#options' => array(0 => t('No'), 1 => t('Yes')),
|
||||
'#default_value' => (int) variable_get('clone_menu_links', 0),
|
||||
'#description' => t('Should any menu link for a node also be cloned?'),
|
||||
);
|
||||
$form['basic']['clone_use_node_type_name'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Use node type name in clone link'),
|
||||
'#default_value' => (int) variable_get('clone_use_node_type_name', 0),
|
||||
'#description' => t('If checked, the link to clone the node will contain the node type name, for example, "Clone this article", otherwise it will read "Clone content".'),
|
||||
);
|
||||
|
||||
$form['publishing'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Should the publishing options ( e.g. published, promoted, etc) be reset to the defaults?'),
|
||||
);
|
||||
$types = node_type_get_names();
|
||||
|
||||
foreach ($types as $type => $name) {
|
||||
$form['publishing']['clone_reset_' . $type] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('@s: reset publishing options when cloned', array('@s' => $name)),
|
||||
'#default_value' => variable_get('clone_reset_' . $type, FALSE),
|
||||
);
|
||||
}
|
||||
|
||||
// Need the variable default key to be something that's never a valid node type.
|
||||
$form['omit'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Content types that are not to be cloned - omitted due to incompatibility'),
|
||||
);
|
||||
$form['omit']['clone_omitted'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Omitted content types'),
|
||||
'#default_value' => variable_get('clone_omitted', array()),
|
||||
'#options' => $types,
|
||||
'#description' => t('Select any node types which should <em>never</em> be cloned. In other words, all node types where cloning will fail.'),
|
||||
);
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback: prompt the user to confirm the operation
|
||||
*/
|
||||
function clone_node_check($node) {
|
||||
|
||||
$method = variable_get('clone_method', 'prepopulate');
|
||||
|
||||
switch ($method) {
|
||||
case 'save-edit':
|
||||
if (variable_get('clone_nodes_without_confirm', FALSE)) {
|
||||
$new_nid = clone_node_save($node->nid);
|
||||
$options = array();
|
||||
if (!empty($_GET['node-clone-destination'])) {
|
||||
$options['query']['destination'] = $_GET['node-clone-destination'];
|
||||
}
|
||||
drupal_goto('node/' . $new_nid . '/edit', $options);
|
||||
}
|
||||
else {
|
||||
return drupal_get_form('clone_node_confirm', $node);
|
||||
}
|
||||
break;
|
||||
case 'prepopulate':
|
||||
default:
|
||||
return clone_node_prepopulate($node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* form builder: prompt the user to confirm the operation
|
||||
*/
|
||||
function clone_node_confirm($form, &$form_state, $node) {
|
||||
|
||||
$form['nid'] = array('#type' => 'value', '#value' => $node->nid);
|
||||
return confirm_form($form,
|
||||
t('Are you sure you want to clone %title?', array('%title' => $node->title)),
|
||||
'node/' . $node->nid, '<p>' . t('This action will create a new node. You will then be redirected to the edit page for the new node.') . '</p>',
|
||||
t('Clone'), t('Cancel'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle confirm form submission
|
||||
*/
|
||||
function clone_node_confirm_submit($form, &$form_state) {
|
||||
if ($form_state['values']['confirm']) {
|
||||
$new_nid = clone_node_save($form_state['values']['nid']);
|
||||
}
|
||||
$form_state['redirect'] = 'node/' . $new_nid . '/edit';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new menu link cloned from another node.
|
||||
*
|
||||
* Returns NULL if no existing link, or links are not to be cloned.
|
||||
*/
|
||||
function clone_node_clone_menu_link($node) {
|
||||
if (variable_get('clone_menu_links', FALSE) && function_exists('menu_node_prepare')) {
|
||||
// This will fetch the existing menu link if the node had one.
|
||||
menu_node_prepare($node);
|
||||
|
||||
if (!empty($node->menu['mlid'])) {
|
||||
$old_link = $node->menu;
|
||||
$link['link_title'] = t('Clone of !title', array('!title' => $old_link['link_title']));
|
||||
$link['plid'] = $old_link['plid'];
|
||||
$link['menu_name'] = $old_link['menu_name'];
|
||||
$link['weight'] = $old_link['weight'];
|
||||
$link['module'] = $old_link['module'];
|
||||
return $link;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a node - prepopulate a node editing form
|
||||
*/
|
||||
function clone_node_prepopulate($original_node) {
|
||||
if (isset($original_node->nid)) {
|
||||
|
||||
if (clone_is_permitted($original_node->type)) {
|
||||
$node = _clone_node_prepare($original_node, TRUE);
|
||||
drupal_set_title($node->title);
|
||||
// Let other modules do special fixing up.
|
||||
$context = array('method' => 'prepopulate', 'original_node' => $original_node);
|
||||
drupal_alter('clone_node', $node, $context);
|
||||
// Make sure the file defining the node form is loaded.
|
||||
$form_state = array();
|
||||
$form_state['build_info']['args'] = array($node);
|
||||
form_load_include($form_state, 'inc', 'node', 'node.pages');
|
||||
return drupal_build_form($node->type .'_node_form', $form_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a node by directly saving it.
|
||||
*/
|
||||
function clone_node_save($nid, $account = NULL) {
|
||||
if (is_numeric($nid)) {
|
||||
|
||||
$original_node = node_load($nid);
|
||||
if (isset($original_node->nid) && clone_is_permitted($original_node->type)) {
|
||||
$node = _clone_node_prepare($original_node, TRUE, $account);
|
||||
// Let other modules do special fixing up.
|
||||
$context = array('method' => 'save-edit', 'original_node' => $original_node);
|
||||
drupal_alter('clone_node', $node, $context);
|
||||
|
||||
node_save($node);
|
||||
if (module_exists('rules')) {
|
||||
rules_invoke_event('clone_node', $node, $original_node);
|
||||
}
|
||||
return $node->nid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a node to be cloned.
|
||||
*/
|
||||
function _clone_node_prepare($original_node, $prefix_title = FALSE, $account = NULL) {
|
||||
$node = clone $original_node;
|
||||
|
||||
if (!isset($account->uid)) {
|
||||
$account = $GLOBALS['user'];
|
||||
}
|
||||
|
||||
$node->nid = NULL;
|
||||
$node->vid = NULL;
|
||||
$node->tnid = NULL;
|
||||
$node->log = NULL;
|
||||
// Also handle modules that attach a UUID to the node.
|
||||
$node->uuid = NULL;
|
||||
$node->vuuid = NULL;
|
||||
|
||||
// Anyonmymous users don't have a name.
|
||||
// @see: drupal_anonymous_user().
|
||||
$node->name = isset($account->name) ? $account->name : NULL;
|
||||
$node->uid = $account->uid;
|
||||
|
||||
$node->created = NULL;
|
||||
$node->menu = clone_node_clone_menu_link($original_node);
|
||||
if (isset($node->book['mlid'])) {
|
||||
$node->book['mlid'] = NULL;
|
||||
$node->book['has_children'] = 0;
|
||||
}
|
||||
$node->path = NULL;
|
||||
$node->files = array();
|
||||
if ($prefix_title) {
|
||||
$node->title = t('Clone of !title', array('!title' => $node->title));
|
||||
}
|
||||
// Add an extra property as a flag.
|
||||
$node->clone_from_original_nid = $original_node->nid;
|
||||
|
||||
if (variable_get('clone_reset_'. $node->type, FALSE)) {
|
||||
$node_options = variable_get('node_options_'. $node->type, array('status', 'promote'));
|
||||
// Fill in the default values.
|
||||
foreach (array('status', 'moderate', 'promote', 'sticky', 'revision') as $key) {
|
||||
// Cast to int since that's how they need to be saved to the DB.
|
||||
$node->$key = (int) in_array($key, $node_options);
|
||||
}
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
23
sites/all/modules/contrib/content/node_clone/clone.rules.inc
Normal file
23
sites/all/modules/contrib/content/node_clone/clone.rules.inc
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file Node clone rules functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of hook_rules_event_info.
|
||||
*/
|
||||
function clone_rules_event_info() {
|
||||
// Let rules know about the node clone event.
|
||||
$items = array(
|
||||
'clone_node' => array(
|
||||
'label' => t('After cloning a node'),
|
||||
'group' => t('Node'),
|
||||
'variables' => array(
|
||||
'cloned_node' => array('type' => 'node', 'label' => t('The cloned node')),
|
||||
'original_node' => array('type' => 'node', 'label' => t('The original node')),
|
||||
),
|
||||
),
|
||||
);
|
||||
return $items;
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Views settings for Node_Clone module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data_alter()
|
||||
*/
|
||||
function clone_views_data_alter(&$views_data) {
|
||||
$views_data['node']['clone_node'] = array(
|
||||
'field' => array(
|
||||
'title' => t('Clone link'),
|
||||
'help' => t('Provide a simple link to clone the node.'),
|
||||
'handler' => 'views_handler_field_node_link_clone',
|
||||
),
|
||||
);
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Views field handler for Node_Clone module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Field handler to present a clone node link.
|
||||
*
|
||||
* Closely modeled after views/modules/node/views_handler_field_node_link_edit.inc
|
||||
*/
|
||||
class views_handler_field_node_link_clone extends views_handler_field_node_link {
|
||||
/**
|
||||
* Renders the link.
|
||||
*/
|
||||
function render_link($node, $values) {
|
||||
|
||||
if (!clone_access_cloning($node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->options['alter']['make_link'] = TRUE;
|
||||
$this->options['alter']['path'] = "node/{$node->nid}/clone/" . clone_token_to_arg();
|
||||
$method = variable_get('clone_method', 'prepopulate');
|
||||
$destination = drupal_get_destination();
|
||||
if ($method == 'prepopulate') {
|
||||
$this->options['alter']['query'] = $destination;
|
||||
}
|
||||
elseif (!empty($destination['destination'])) {
|
||||
$this->options['alter']['query']['node-clone-destination'] = $destination['destination'];
|
||||
}
|
||||
|
||||
$text = !empty($this->options['text']) ? $this->options['text'] : t('clone');
|
||||
return $text;
|
||||
}
|
||||
}
|
149
sites/all/modules/contrib/content/pathauto/API.txt
Normal file
149
sites/all/modules/contrib/content/pathauto/API.txt
Normal file
@@ -0,0 +1,149 @@
|
||||
This document explains how to provide "Pathauto integration" in a
|
||||
module. You need this if you would like to provide additional tokens
|
||||
or if your module has paths and you wish to have them automatically
|
||||
aliased. The simplest integration is just to provide tokens so we
|
||||
cover that first. More advanced integration requires an
|
||||
implementation of hook_pathauto to provide a settings form.
|
||||
|
||||
It may be helpful to review some examples of integration from the
|
||||
pathauto_node.inc, pathauto_taxonomy.inc, and pathauto_user.inc files.
|
||||
|
||||
|
||||
==================
|
||||
1 - Providing additional tokens
|
||||
==================
|
||||
|
||||
If all you want is to enable tokens for your module you will simply
|
||||
need to implement two functions:
|
||||
|
||||
hook_token_values
|
||||
hook_token_list
|
||||
|
||||
See the token.module and it's API.txt for more information about this
|
||||
process.
|
||||
|
||||
If the token is intended to generate a path expected to contain slashes,
|
||||
the token name must end in 'path', 'path-raw' or 'alias'. This indicates to
|
||||
Pathauto that the slashes should not be removed from the replacement value.
|
||||
|
||||
When an object is created (whether it is a node or a user or a
|
||||
taxonomy term) the data that Pathauto hands to the token_values in the
|
||||
$object is in a specific format. This is the format that most people
|
||||
write code to handle. However, during edits and bulk updates the data
|
||||
may be in a totally different format. So, if you are writing a
|
||||
hook_token_values implementation to add special tokens, be sure to
|
||||
test creation, edit, and bulk update cases to make sure your code will
|
||||
handle it.
|
||||
|
||||
==================
|
||||
2 - Settings hook - To create aliases for your module
|
||||
==================
|
||||
You must implement hook_pathauto($op), where $op is always (at this
|
||||
time) 'settings'. Return an object (NOT an array) containing the
|
||||
following members, which will be used by pathauto to build a group
|
||||
of settings for your module and define the variables for saving your
|
||||
settings:
|
||||
|
||||
module - The name of your module (e.g., 'node')
|
||||
groupheader - The translated label for the settings group (e.g.,
|
||||
t('Content path settings')
|
||||
patterndescr - The translated label for the default pattern (e.g.,
|
||||
t('Default path pattern (applies to all content types with blank patterns below)')
|
||||
patterndefault - A translated default pattern (e.g., t('[cat]/[title].html'))
|
||||
token_type - The token type (e.g. 'node', 'user') that can be used.
|
||||
patternitems - For modules which need to express multiple patterns
|
||||
(for example, the node module supports a separate pattern for each
|
||||
content type), an array whose keys consist of identifiers for each
|
||||
pattern (e.g., the content type name) and values consist of the
|
||||
translated label for the pattern
|
||||
bulkname - For modules which support a bulk update operation, the
|
||||
translated label for the action (e.g., t('Bulk update content paths'))
|
||||
bulkdescr - For modules which support a bulk update operation, a
|
||||
translated, more thorough description of what the operation will do
|
||||
(e.g., t('Generate aliases for all existing content items which do not already have aliases.'))
|
||||
|
||||
|
||||
==================
|
||||
2 - $alias = pathauto_create_alias($module, $op, $placeholders, $src, $type=NULL)
|
||||
==================
|
||||
|
||||
At the appropriate time (usually when a new item is being created for
|
||||
which a generated alias is desired), call pathauto_create_alias() to
|
||||
generate and create the alias. See the user, taxonomy, and nodeapi hook
|
||||
implementations in pathauto.module for examples.
|
||||
|
||||
$module - The name of your module (e.g., 'node')
|
||||
$op - Operation being performed on the item ('insert', 'update', or
|
||||
'bulkupdate')
|
||||
$placeholders - An array whose keys consist of the translated placeholders
|
||||
which appear in patterns and values are the "clean" values to be
|
||||
substituted into the pattern. Call pathauto_cleanstring() on any
|
||||
values which you do not know to be purely alphanumeric, to substitute
|
||||
any non-alphanumerics with the user's designated separator. Note that
|
||||
if the pattern has multiple slash-separated components (e.g., [term:path]),
|
||||
pathauto_cleanstring() should be called for each component, not the
|
||||
complete string.
|
||||
Example: $placeholders[t('[title]')] = pathauto_cleanstring($node->title);
|
||||
$src - The "real" URI of the content to be aliased (e.g., "node/$node->nid")
|
||||
$type - For modules which provided patternitems in hook_autopath(),
|
||||
the relevant identifier for the specific item to be aliased (e.g.,
|
||||
$node->type)
|
||||
|
||||
pathauto_create_alias() returns the alias that was created.
|
||||
|
||||
|
||||
==================
|
||||
3 - Bulk update function
|
||||
==================
|
||||
|
||||
If a module supports bulk updating of aliases, it must provide a
|
||||
function of this form, to be called by pathauto when the corresponding
|
||||
checkbox is selected and the settings page submitted:
|
||||
|
||||
function <module>_pathauto_bulkupdate()
|
||||
|
||||
The function should iterate over the content items controlled by the
|
||||
module, calling pathauto_create_alias() for each one. It is
|
||||
recommended that the function report on its success (e.g., with a
|
||||
count of created aliases) via drupal_set_message().
|
||||
|
||||
|
||||
==================
|
||||
4 - Bulk delete hook_path_alias_types()
|
||||
==================
|
||||
|
||||
For modules that create new types of pages that can be aliased with pathauto, a
|
||||
hook implementation is needed to allow the user to delete them all at once.
|
||||
|
||||
function hook_path_alias_types()
|
||||
|
||||
This hook returns an array whose keys match the beginning of the source paths
|
||||
(e.g.: "node/", "user/", etc.) and whose values describe the type of page (e.g.:
|
||||
"content", "users"). Like all displayed strings, these descriptionsshould be
|
||||
localized with t(). Use % to match interior pieces of a path; "user/%/track". This
|
||||
is a database wildcard, so be careful.
|
||||
|
||||
|
||||
==================
|
||||
Modules that extend node and/or taxonomy
|
||||
==================
|
||||
|
||||
NOTE: this is basically not true any more. If you feel you need this file an issue.
|
||||
|
||||
Many contributed Drupal modules extend the core node and taxonomy
|
||||
modules. To extend pathauto patterns to support their extensions, they
|
||||
may implement the pathauto_node and pathauto_taxonomy hooks.
|
||||
|
||||
To do so, implement the function <modulename>_pathauto_node (or _taxonomy),
|
||||
accepting the arguments $op and $node (or $term). Two operations are
|
||||
supported:
|
||||
|
||||
$op = 'placeholders' - return an array keyed on placeholder strings
|
||||
(e.g., t('[eventyyyy]')) valued with descriptions (e.g. t('The year the
|
||||
event starts.')).
|
||||
$op = 'values' - return an array keyed on placeholder strings, valued
|
||||
with the "clean" actual value for the passed node or category (e.g.,
|
||||
pathauto_cleanstring(date('M', $eventstart)));
|
||||
|
||||
See contrib/pathauto_node_event.inc for an example of extending node
|
||||
patterns.
|
48
sites/all/modules/contrib/content/pathauto/INSTALL.txt
Normal file
48
sites/all/modules/contrib/content/pathauto/INSTALL.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
**Installation:
|
||||
|
||||
Pathauto is an extension to the path module, which must be enabled.
|
||||
|
||||
Pathauto also relies on the Token module, which must be downloaded and
|
||||
enabled separately.
|
||||
|
||||
1. Unpack the Pathauto folder and contents in the appropriate modules
|
||||
directory of your Drupal installation. This is probably
|
||||
sites/all/modules/
|
||||
2. Enable the Pathauto module in the administration tools.
|
||||
3. If you're not using Drupal's default administrative account, make
|
||||
sure "administer pathauto" is enabled through access control administration.
|
||||
4. Visit the Pathauto settings page and make appropriate configurations
|
||||
For 5.x: Administer > Site configuration > Pathauto
|
||||
For 6.x: Administer > Site building > URL alias > Automated alias settings
|
||||
|
||||
**Transliteration support:
|
||||
If you desire transliteration support in the creation of URLs (e.g. the
|
||||
replacement of Á with A) then you will need to install the Transliteration
|
||||
module, which can be found at http://drupal.org/project/transliteration
|
||||
|
||||
Once you've installed and enabled the module, simply go to
|
||||
admin/config/search/path/settings and check the "Transliterate prior to
|
||||
creating alias" box and path aliases should now be transliterated automagically.
|
||||
|
||||
**Upgrading from previous versions:
|
||||
If you are upgrading from Pathauto 5.x-1.x to 5.x-2.x (or 6.x-2.x) then you
|
||||
will probably need to change your patterns.
|
||||
|
||||
For content patterns:
|
||||
[user] is now [author-name]
|
||||
[cat] is now [term]
|
||||
|
||||
There may be other changes as well. Please review the pattern examples on
|
||||
Administration > Site Configuration > Pathauto
|
||||
|
||||
If you upgraded from Pathauto 5.x-1.x directly without enabling Token
|
||||
first then you will need to
|
||||
1) download/install the Token module
|
||||
2) disable the Pathauto module
|
||||
3) re-enable the Pathauto module
|
||||
|
||||
Upgrade to 6.x:
|
||||
Note that the settings page has moved so that it is more logically grouped with
|
||||
other URL alias related items under
|
||||
Administer > Site building > URL alias > Automated alias settings
|
||||
|
339
sites/all/modules/contrib/content/pathauto/LICENSE.txt
Normal file
339
sites/all/modules/contrib/content/pathauto/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.
|
94
sites/all/modules/contrib/content/pathauto/README.txt
Normal file
94
sites/all/modules/contrib/content/pathauto/README.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
Please read this file and also the INSTALL.txt.
|
||||
They contain answers to many common questions.
|
||||
If you are developing for this module, the API.txt may be interesting.
|
||||
If you are upgrading, check the CHANGELOG.txt for major changes.
|
||||
|
||||
**Description:
|
||||
The Pathauto module provides support functions for other modules to
|
||||
automatically generate aliases based on appropriate criteria, with a
|
||||
central settings path for site administrators.
|
||||
|
||||
Implementations are provided for core entity types: content, taxonomy terms,
|
||||
and users (including blogs and tracker pages).
|
||||
|
||||
Pathauto also provides a way to delete large numbers of aliases. This feature
|
||||
is available at Administer > Site building > URL aliases > Delete aliases
|
||||
|
||||
**Benefits:
|
||||
Besides making the page address more reflective of its content than
|
||||
"node/138", it's important to know that modern search engines give
|
||||
heavy weight to search terms which appear in a page's URL. By
|
||||
automatically using keywords based directly on the page content in the URL,
|
||||
relevant search engine hits for your page can be significantly
|
||||
enhanced.
|
||||
|
||||
**Installation AND Upgrades:
|
||||
See the INSTALL.txt file.
|
||||
|
||||
**Notices:
|
||||
Pathauto just adds URL aliases to content, users, and taxonomy terms.
|
||||
Because it's an alias, the standard Drupal URL (for example node/123 or
|
||||
taxonomy/term/1) will still function as normal. If you have external links
|
||||
to your site pointing to standard Drupal URLs, or hardcoded links in a module,
|
||||
template, content or menu which point to standard Drupal URLs it will bypass
|
||||
the alias set by Pathauto.
|
||||
|
||||
There are reasons you might not want two URLs for the same content on your
|
||||
site. If this applies to you, please note that you will need to update any
|
||||
hard coded links in your content or blocks.
|
||||
|
||||
If you use the "system path" (i.e. node/10) for menu items and settings like
|
||||
that, Drupal will replace it with the url_alias.
|
||||
|
||||
For external links, you might want to consider the Path Redirect or
|
||||
Global Redirect modules, which allow you to set forwarding either per item or
|
||||
across the site to your aliased URLs.
|
||||
|
||||
URLs (not) Getting Replaced With Aliases:
|
||||
Please bear in mind that only URLs passed through Drupal's l() or url()
|
||||
functions will be replaced with their aliases during page output. If a module
|
||||
or your template contains hardcoded links, such as 'href="node/$node->nid"'
|
||||
those won't get replaced with their corresponding aliases. Use the
|
||||
Drupal API instead:
|
||||
|
||||
* 'href="'. url("node/$node->nid") .'"' or
|
||||
* l("Your link title", "node/$node->nid")
|
||||
|
||||
See http://api.drupal.org/api/HEAD/function/url and
|
||||
http://api.drupal.org/api/HEAD/function/l for more information.
|
||||
|
||||
** Disabling Pathauto for a specific content type (or taxonomy)
|
||||
When the pattern for a content type is left blank, the default pattern will be
|
||||
used. But if the default pattern is also blank, Pathauto will be disabled
|
||||
for that content type.
|
||||
|
||||
** Bulk Updates Must be Run Multiple Times:
|
||||
As of 5.x-2.x Pathauto now performs bulk updates in a manner which is more
|
||||
likely to succeed on large sites. The drawback is that it needs to be run
|
||||
multiple times. If you want to reduce the number of times that you need to
|
||||
run Pathauto you can increase the "Maximum number of objects to alias in a
|
||||
bulk update:" setting under General Settings.
|
||||
|
||||
**WYSIWYG Conflicts - FCKEditor, TinyMCE, etc.
|
||||
If you use a WYSIWYG editor, please disable it for the Pathauto admin page.
|
||||
Failure to do so may cause errors about "preg_replace" problems due to the <p>
|
||||
tag being added to the "strings to replace". See http://drupal.org/node/175772
|
||||
|
||||
**Credits:
|
||||
The original module combined the functionality of Mike Ryan's autopath with
|
||||
Tommy Sundstrom's path_automatic.
|
||||
|
||||
Significant enhancements were contributed by jdmquin @ www.bcdems.net.
|
||||
|
||||
Matt England added the tracker support.
|
||||
|
||||
Other suggestions and patches contributed by the Drupal community.
|
||||
|
||||
Current maintainers:
|
||||
Greg Knaddison - http://growingventuresolutions.com
|
||||
Mike Ryan - http://mikeryan.name
|
||||
Frederik 'Freso' S. Olesen - http://freso.dk
|
||||
|
||||
**Changes:
|
||||
See the CHANGELOG.txt file.
|
||||
|
437
sites/all/modules/contrib/content/pathauto/pathauto.admin.inc
Normal file
437
sites/all/modules/contrib/content/pathauto/pathauto.admin.inc
Normal file
@@ -0,0 +1,437 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Admin page callbacks for the Pathauto module.
|
||||
*
|
||||
* @ingroup pathauto
|
||||
*/
|
||||
|
||||
/**
|
||||
* Form builder; Configure the URL alias patterns.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see system_settings_form()
|
||||
*/
|
||||
function pathauto_patterns_form($form, $form_state) {
|
||||
// Call the hook on all modules - an array of 'settings' objects is returned
|
||||
$all_settings = module_invoke_all('pathauto', 'settings');
|
||||
foreach ($all_settings as $settings) {
|
||||
$module = $settings->module;
|
||||
$patterndescr = $settings->patterndescr;
|
||||
$patterndefault = $settings->patterndefault;
|
||||
$groupheader = $settings->groupheader;
|
||||
|
||||
$form[$module] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $groupheader,
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE,
|
||||
);
|
||||
|
||||
// Prompt for the default pattern for this module
|
||||
$variable = 'pathauto_' . $module . '_pattern';
|
||||
$form[$module][$variable] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $patterndescr,
|
||||
'#default_value' => variable_get($variable, $patterndefault),
|
||||
'#size' => 65,
|
||||
'#maxlength' => 1280,
|
||||
'#element_validate' => array('token_element_validate'),
|
||||
'#after_build' => array('token_element_validate'),
|
||||
'#token_types' => array($settings->token_type),
|
||||
'#min_tokens' => 1,
|
||||
'#parents' => array($variable),
|
||||
);
|
||||
|
||||
// If the module supports a set of specialized patterns, set
|
||||
// them up here
|
||||
if (isset($settings->patternitems)) {
|
||||
foreach ($settings->patternitems as $itemname => $itemlabel) {
|
||||
$variable = 'pathauto_' . $module . '_' . $itemname . '_pattern';
|
||||
$form[$module][$variable] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $itemlabel,
|
||||
'#default_value' => variable_get($variable, ''),
|
||||
'#size' => 65,
|
||||
'#maxlength' => 1280,
|
||||
'#element_validate' => array('token_element_validate'),
|
||||
'#after_build' => array('token_element_validate'),
|
||||
'#token_types' => array($settings->token_type),
|
||||
'#min_tokens' => 1,
|
||||
'#parents' => array($variable),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Display the user documentation of placeholders supported by
|
||||
// this module, as a description on the last pattern
|
||||
$form[$module]['token_help'] = array(
|
||||
'#title' => t('Replacement patterns'),
|
||||
'#type' => 'fieldset',
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
$form[$module]['token_help']['help'] = array(
|
||||
'#theme' => 'token_tree',
|
||||
'#token_types' => array($settings->token_type),
|
||||
);
|
||||
}
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form builder; Configure the Pathauto settings.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see system_settings_form()
|
||||
*/
|
||||
function pathauto_settings_form($form) {
|
||||
module_load_include('inc', 'pathauto');
|
||||
|
||||
$form['pathauto_verbose'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Verbose'),
|
||||
'#default_value' => variable_get('pathauto_verbose', FALSE),
|
||||
'#description' => t('Display alias changes (except during bulk updates).'),
|
||||
);
|
||||
|
||||
$form['pathauto_separator'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Separator'),
|
||||
'#size' => 1,
|
||||
'#maxlength' => 1,
|
||||
'#default_value' => variable_get('pathauto_separator', '-'),
|
||||
'#description' => t('Character used to separate words in titles. This will replace any spaces and punctuation characters. Using a space or + character can cause unexpected results.'),
|
||||
);
|
||||
|
||||
$form['pathauto_case'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Character case'),
|
||||
'#default_value' => variable_get('pathauto_case', PATHAUTO_CASE_LOWER),
|
||||
'#options' => array(
|
||||
PATHAUTO_CASE_LEAVE_ASIS => t('Leave case the same as source token values.'),
|
||||
PATHAUTO_CASE_LOWER => t('Change to lower case'),
|
||||
),
|
||||
);
|
||||
|
||||
$max_length = _pathauto_get_schema_alias_maxlength();
|
||||
|
||||
$form['pathauto_max_length'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Maximum alias length'),
|
||||
'#size' => 3,
|
||||
'#maxlength' => 3,
|
||||
'#default_value' => variable_get('pathauto_max_length', 100),
|
||||
'#min_value' => 1,
|
||||
'#max_value' => $max_length,
|
||||
'#description' => t('Maximum length of aliases to generate. 100 is the recommended length. @max is the maximum possible length. See <a href="@pathauto-help">Pathauto help</a> for details.', array('@pathauto-help' => url('admin/help/pathauto'), '@max' => $max_length)),
|
||||
'#element_validate' => array('_pathauto_validate_numeric_element'),
|
||||
);
|
||||
$form['pathauto_max_component_length'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Maximum component length'),
|
||||
'#size' => 3,
|
||||
'#maxlength' => 3,
|
||||
'#default_value' => variable_get('pathauto_max_component_length', 100),
|
||||
'#min_value' => 1,
|
||||
'#max_value' => $max_length,
|
||||
'#description' => t('Maximum text length of any component in the alias (e.g., [title]). 100 is the recommended length. @max is the maximum possible length. See <a href="@pathauto-help">Pathauto help</a> for details.', array('@pathauto-help' => url('admin/help/pathauto'), '@max' => $max_length)),
|
||||
'#element_validate' => array('_pathauto_validate_numeric_element'),
|
||||
);
|
||||
|
||||
|
||||
$description = t('What should Pathauto do when updating an existing content item which already has an alias?');
|
||||
if (module_exists('redirect')) {
|
||||
$description .= ' ' . t('The <a href="!url">Redirect module settings</a> affect whether a redirect is created when an alias is deleted.', array('!url' => url('admin/config/search/redirect')));
|
||||
}
|
||||
else {
|
||||
$description .= ' ' . t('Considering installing the <a href="!url">Redirect module</a> to get redirects when your aliases change.', array('!url' => 'http://drupal.org/project/redirect'));
|
||||
}
|
||||
$form['pathauto_update_action'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Update action'),
|
||||
'#default_value' => variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE),
|
||||
'#options' => array(
|
||||
PATHAUTO_UPDATE_ACTION_NO_NEW => t('Do nothing. Leave the old alias intact.'),
|
||||
PATHAUTO_UPDATE_ACTION_LEAVE => t('Create a new alias. Leave the existing alias functioning.'),
|
||||
PATHAUTO_UPDATE_ACTION_DELETE => t('Create a new alias. Delete the old alias.'),
|
||||
),
|
||||
'#description' => $description,
|
||||
);
|
||||
|
||||
$form['pathauto_transliterate'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Transliterate prior to creating alias'),
|
||||
'#default_value' => variable_get('pathauto_transliterate', FALSE) && module_exists('transliteration'),
|
||||
'#description' => t('When a pattern includes certain characters (such as those with accents) should Pathauto attempt to transliterate them into the ASCII-96 alphabet? Transliteration is handled by the Transliteration module.'),
|
||||
'#access' => module_exists('transliteration'),
|
||||
);
|
||||
|
||||
$form['pathauto_reduce_ascii'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Reduce strings to letters and numbers'),
|
||||
'#default_value' => variable_get('pathauto_reduce_ascii', FALSE),
|
||||
'#description' => t('Filters the new alias to only letters and numbers found in the ASCII-96 set.'),
|
||||
);
|
||||
|
||||
$form['pathauto_ignore_words'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Strings to Remove'),
|
||||
'#default_value' => variable_get('pathauto_ignore_words', PATHAUTO_IGNORE_WORDS),
|
||||
'#description' => t('Words to strip out of the URL alias, separated by commas. Do not use this to remove punctuation.'),
|
||||
'#wysiwyg' => FALSE,
|
||||
);
|
||||
|
||||
$form['punctuation'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Punctuation'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
|
||||
$punctuation = pathauto_punctuation_chars();
|
||||
foreach ($punctuation as $name => $details) {
|
||||
$details['default'] = PATHAUTO_PUNCTUATION_REMOVE;
|
||||
if ($details['value'] == variable_get('pathauto_separator', '-')) {
|
||||
$details['default'] = PATHAUTO_PUNCTUATION_REPLACE;
|
||||
}
|
||||
$form['punctuation']['pathauto_punctuation_' . $name] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $details['name'] . ' (<code>' . check_plain($details['value']) . '</code>)',
|
||||
'#default_value' => variable_get('pathauto_punctuation_' . $name, $details['default']),
|
||||
'#options' => array(
|
||||
PATHAUTO_PUNCTUATION_REMOVE => t('Remove'),
|
||||
PATHAUTO_PUNCTUATION_REPLACE => t('Replace by separator'),
|
||||
PATHAUTO_PUNCTUATION_DO_NOTHING => t('No action (do not replace)'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a form element that should have an numeric value.
|
||||
*/
|
||||
function _pathauto_validate_numeric_element($element, &$form_state) {
|
||||
$value = $element['#value'];
|
||||
|
||||
if (!is_numeric($value)) {
|
||||
form_error($element, t('The field %name is not a valid number.', array('%name' => $element['#title'])));
|
||||
}
|
||||
elseif (isset($element['#max_value']) && $value > $element['#max_value']) {
|
||||
form_error($element, t('The field %name cannot be greater than @max.', array('%name' => $element['#title'], '@max' => $element['#max_value'])));
|
||||
}
|
||||
elseif (isset($element['#min_value']) && $value < $element['#min_value']) {
|
||||
form_error($element, t('The field %name cannot be less than @min.', array('%name' => $element['#title'], '@min' => $element['#min_value'])));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate pathauto_settings_form form submissions.
|
||||
*/
|
||||
function pathauto_settings_form_validate($form, &$form_state) {
|
||||
module_load_include('inc', 'pathauto');
|
||||
|
||||
// Perform a basic check for HTML characters in the strings to remove field.
|
||||
if (strip_tags($form_state['values']['pathauto_ignore_words']) != $form_state['values']['pathauto_ignore_words']) {
|
||||
form_set_error('pathauto_ignore_words', t('The <em>Strings to remove</em> field must not contain HTML. Make sure to disable any WYSIWYG editors for this field.'));
|
||||
}
|
||||
|
||||
// Validate that the separator is not set to be removed per http://drupal.org/node/184119
|
||||
// This isn't really all that bad so warn, but still allow them to save the value.
|
||||
$separator = $form_state['values']['pathauto_separator'];
|
||||
$punctuation = pathauto_punctuation_chars();
|
||||
foreach ($punctuation as $name => $details) {
|
||||
if ($details['value'] == $separator) {
|
||||
$action = $form_state['values']['pathauto_punctuation_' . $name];
|
||||
if ($action == PATHAUTO_PUNCTUATION_REMOVE) {
|
||||
drupal_set_message(t('You have configured the @name to be the separator and to be removed when encountered in strings. This can cause problems with your patterns and especially with the term:path token. You should probably set the action for @name to be "replace by separator".', array('@name' => $details['name'])), 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form contructor for path alias bulk update form.
|
||||
*
|
||||
* @see pathauto_bulk_update_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function pathauto_bulk_update_form() {
|
||||
$form['#update_callbacks'] = array();
|
||||
|
||||
$form['update'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Select the types of un-aliased paths for which to generate URL aliases'),
|
||||
'#options' => array(),
|
||||
'#default_value' => array(),
|
||||
);
|
||||
|
||||
$pathauto_settings = module_invoke_all('pathauto', 'settings');
|
||||
foreach ($pathauto_settings as $settings) {
|
||||
if (!empty($settings->batch_update_callback)) {
|
||||
$form['#update_callbacks'][$settings->batch_update_callback] = $settings;
|
||||
$form['update']['#options'][$settings->batch_update_callback] = $settings->groupheader;
|
||||
}
|
||||
}
|
||||
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submit handler for path alias bulk update form.
|
||||
*
|
||||
* @see pathauto_batch_update_form()
|
||||
* @see pathauto_bulk_update_batch_finished()
|
||||
*/
|
||||
function pathauto_bulk_update_form_submit($form, &$form_state) {
|
||||
$batch = array(
|
||||
'title' => t('Bulk updating URL aliases'),
|
||||
'operations' => array(
|
||||
array('pathauto_bulk_update_batch_start', array()),
|
||||
),
|
||||
'finished' => 'pathauto_bulk_update_batch_finished',
|
||||
'file' => drupal_get_path('module', 'pathauto') . '/pathauto.admin.inc',
|
||||
);
|
||||
|
||||
foreach ($form_state['values']['update'] as $callback) {
|
||||
if (!empty($callback)) {
|
||||
$settings = $form['#update_callbacks'][$callback];
|
||||
if (!empty($settings->batch_file)) {
|
||||
$batch['operations'][] = array('pathauto_bulk_update_batch_process', array($callback, $settings));
|
||||
}
|
||||
else {
|
||||
$batch['operations'][] = array($callback, array());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
batch_set($batch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; count the current number of URL aliases for comparison later.
|
||||
*/
|
||||
function pathauto_bulk_update_batch_start(&$context) {
|
||||
$context['results']['count_before'] = db_select('url_alias')->countQuery()->execute()->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Common batch processing callback for all operations.
|
||||
*
|
||||
* Required to load our include the proper batch file.
|
||||
*/
|
||||
function pathauto_bulk_update_batch_process($callback, $settings, &$context) {
|
||||
if (!empty($settings->batch_file)) {
|
||||
require_once DRUPAL_ROOT . '/' . $settings->batch_file;
|
||||
}
|
||||
return $callback($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch finished callback.
|
||||
*/
|
||||
function pathauto_bulk_update_batch_finished($success, $results, $operations) {
|
||||
if ($success) {
|
||||
// Count the current number of URL aliases after the batch is completed
|
||||
// and compare to the count before the batch started.
|
||||
$results['count_after'] = db_select('url_alias')->countQuery()->execute()->fetchField();
|
||||
$results['count_changed'] = max($results['count_after'] - $results['count_before'], 0);
|
||||
if ($results['count_changed']) {
|
||||
drupal_set_message(format_plural($results['count_changed'], 'Generated 1 URL alias.', 'Generated @count URL aliases.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('No new URL aliases to generate.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$error_operation = reset($operations);
|
||||
drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; select certain alias types to delete.
|
||||
*/
|
||||
function pathauto_admin_delete() {
|
||||
/* TODO:
|
||||
1) all - DONE
|
||||
2) all node aliases - DONE
|
||||
4) all user aliases - DONE
|
||||
5) all taxonomy aliases - DONE
|
||||
6) by node type
|
||||
7) by taxonomy vocabulary
|
||||
8) no longer existing aliases (see http://drupal.org/node/128366 )
|
||||
9) where src like 'pattern' - DON'T DO
|
||||
10) where dst like 'pattern' - DON'T DO
|
||||
*/
|
||||
|
||||
$form['delete'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Choose aliases to delete'),
|
||||
'#collapsible' => FALSE,
|
||||
'#collapsed' => FALSE,
|
||||
);
|
||||
|
||||
// First we do the "all" case
|
||||
$total_count = db_query('SELECT count(1) FROM {url_alias}')->fetchField();
|
||||
$form['delete']['all_aliases'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('All aliases'),
|
||||
'#default_value' => FALSE,
|
||||
'#description' => t('Delete all aliases. Number of aliases which will be deleted: %count.', array('%count' => $total_count)),
|
||||
);
|
||||
|
||||
// Next, iterate over an array of objects/alias types which can be deleted and provide checkboxes
|
||||
$objects = module_invoke_all('path_alias_types');
|
||||
foreach ($objects as $internal_name => $label) {
|
||||
$count = db_query("SELECT count(1) FROM {url_alias} WHERE source LIKE :src", array(':src' => "$internal_name%"))->fetchField();
|
||||
$form['delete'][$internal_name] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $label, // This label is sent through t() in the hard coded function where it is defined
|
||||
'#default_value' => FALSE,
|
||||
'#description' => t('Delete aliases for all @label. Number of aliases which will be deleted: %count.', array('@label' => $label, '%count' => $count)),
|
||||
);
|
||||
}
|
||||
|
||||
// Warn them and give a button that shows we mean business
|
||||
$form['warning'] = array('#value' => '<p>' . t('<strong>Note:</strong> there is no confirmation. Be sure of your action before clicking the "Delete aliases now!" button.<br />You may want to make a backup of the database and/or the url_alias table prior to using this feature.') . '</p>');
|
||||
$form['buttons']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Delete aliases now!'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process pathauto_admin_delete form submissions.
|
||||
*/
|
||||
function pathauto_admin_delete_submit($form, &$form_state) {
|
||||
foreach ($form_state['values'] as $key => $value) {
|
||||
if ($value) {
|
||||
if ($key === 'all_aliases') {
|
||||
db_delete('url_alias')
|
||||
->execute();
|
||||
drupal_set_message(t('All of your path aliases have been deleted.'));
|
||||
}
|
||||
$objects = module_invoke_all('path_alias_types');
|
||||
if (array_key_exists($key, $objects)) {
|
||||
db_delete('url_alias')
|
||||
->condition('source', db_like($key) . '%', 'LIKE')
|
||||
->execute();
|
||||
drupal_set_message(t('All of your %type path aliases have been deleted.', array('%type' => $objects[$key])));
|
||||
}
|
||||
}
|
||||
}
|
||||
$form_state['redirect'] = 'admin/config/search/path/delete_bulk';
|
||||
}
|
60
sites/all/modules/contrib/content/pathauto/pathauto.api.php
Normal file
60
sites/all/modules/contrib/content/pathauto/pathauto.api.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Documentation for pathauto API.
|
||||
*
|
||||
* @see hook_token_info
|
||||
* @see hook_tokens
|
||||
*/
|
||||
|
||||
function hook_path_alias_types() {
|
||||
}
|
||||
|
||||
function hook_pathauto($op) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter Pathauto-generated aliases before saving.
|
||||
*
|
||||
* @param string $alias
|
||||
* The automatic alias after token replacement and strings cleaned.
|
||||
* @param array $context
|
||||
* An associative array of additional options, with the following elements:
|
||||
* - 'module': The module or entity type being aliased.
|
||||
* - 'op': A string with the operation being performed on the object being
|
||||
* aliased. Can be either 'insert', 'update', 'return', or 'bulkupdate'.
|
||||
* - 'source': A string of the source path for the alias (e.g. 'node/1').
|
||||
* This can be altered by reference.
|
||||
* - 'data': An array of keyed objects to pass to token_replace().
|
||||
* - 'type': The sub-type or bundle of the object being aliased.
|
||||
* - 'language': A string of the language code for the alias (e.g. 'en').
|
||||
* This can be altered by reference.
|
||||
* - 'pattern': A string of the pattern used for aliasing the object.
|
||||
*/
|
||||
function hook_pathauto_alias_alter(&$alias, array &$context) {
|
||||
// Add a suffix so that all aliases get saved as 'content/my-title.html'
|
||||
$alias .= '.html';
|
||||
|
||||
// Force all aliases to be saved as language neutral.
|
||||
$context['language'] = LANGUAGE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the list of punctuation characters for Pathauto control.
|
||||
*
|
||||
* @param $punctuation
|
||||
* An array of punctuation to be controlled by Pathauto during replacement
|
||||
* keyed by punctuation name. Each punctuation record should be an array
|
||||
* with the following key/value pairs:
|
||||
* - value: The raw value of the punctuation mark.
|
||||
* - name: The human-readable name of the punctuation mark. This must be
|
||||
* translated using t() already.
|
||||
*/
|
||||
function hook_pathauto_punctuation_chars_alter(array &$punctuation) {
|
||||
// Add the trademark symbol.
|
||||
$punctuation['trademark'] = array('value' => '™', 'name' => t('Trademark symbol'));
|
||||
|
||||
// Remove the dollar sign.
|
||||
unset($punctuation['dollar']);
|
||||
}
|
689
sites/all/modules/contrib/content/pathauto/pathauto.inc
Normal file
689
sites/all/modules/contrib/content/pathauto/pathauto.inc
Normal file
@@ -0,0 +1,689 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Miscellaneous functions for Pathauto.
|
||||
*
|
||||
* This also contains some constants giving human readable names to some numeric
|
||||
* settings; they're included here as they're only rarely used outside this file
|
||||
* anyway. Use module_load_include('inc', 'pathauto') if the constants need to
|
||||
* be available.
|
||||
*
|
||||
* @ingroup pathauto
|
||||
*/
|
||||
|
||||
/**
|
||||
* Case should be left as is in the generated path.
|
||||
*/
|
||||
define('PATHAUTO_CASE_LEAVE_ASIS', 0);
|
||||
|
||||
/**
|
||||
* Case should be lowercased in the generated path.
|
||||
*/
|
||||
define('PATHAUTO_CASE_LOWER', 1);
|
||||
|
||||
/**
|
||||
* "Do nothing. Leave the old alias intact."
|
||||
*/
|
||||
define('PATHAUTO_UPDATE_ACTION_NO_NEW', 0);
|
||||
|
||||
/**
|
||||
* "Create a new alias. Leave the existing alias functioning."
|
||||
*/
|
||||
define('PATHAUTO_UPDATE_ACTION_LEAVE', 1);
|
||||
|
||||
/**
|
||||
* "Create a new alias. Delete the old alias."
|
||||
*/
|
||||
define('PATHAUTO_UPDATE_ACTION_DELETE', 2);
|
||||
|
||||
/**
|
||||
* Remove the punctuation from the alias.
|
||||
*/
|
||||
define('PATHAUTO_PUNCTUATION_REMOVE', 0);
|
||||
|
||||
/**
|
||||
* Replace the punctuation with the separator in the alias.
|
||||
*/
|
||||
define('PATHAUTO_PUNCTUATION_REPLACE', 1);
|
||||
|
||||
/**
|
||||
* Leave the punctuation as it is in the alias.
|
||||
*/
|
||||
define('PATHAUTO_PUNCTUATION_DO_NOTHING', 2);
|
||||
|
||||
/**
|
||||
* Check to see if there is already an alias pointing to a different item.
|
||||
*
|
||||
* @param $alias
|
||||
* A string alias.
|
||||
* @param $source
|
||||
* A string that is the internal path.
|
||||
* @param $language
|
||||
* A string indicating the path's language.
|
||||
* @return
|
||||
* TRUE if an alias exists, FALSE if not.
|
||||
*/
|
||||
function _pathauto_alias_exists($alias, $source, $language = LANGUAGE_NONE) {
|
||||
$pid = db_query_range("SELECT pid FROM {url_alias} WHERE source <> :source AND alias = :alias AND language IN (:language, :language_none) ORDER BY language DESC, pid DESC", 0, 1, array(
|
||||
':source' => $source,
|
||||
':alias' => $alias,
|
||||
':language' => $language,
|
||||
':language_none' => LANGUAGE_NONE,
|
||||
))->fetchField();
|
||||
|
||||
return !empty($pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an existing URL alias given a path and optional language.
|
||||
*
|
||||
* @param $source
|
||||
* An internal Drupal path.
|
||||
* @param $language
|
||||
* An optional language code to look up the path in.
|
||||
* @return
|
||||
* FALSE if no alias was found or an associative array containing the
|
||||
* following keys:
|
||||
* - pid: Unique path alias identifier.
|
||||
* - alias: The URL alias.
|
||||
*/
|
||||
function _pathauto_existing_alias_data($source, $language = LANGUAGE_NONE) {
|
||||
$pid = db_query_range("SELECT pid FROM {url_alias} WHERE source = :source AND language IN (:language, :language_none) ORDER BY language DESC, pid DESC", 0, 1, array(':source' => $source, ':language' => $language, ':language_none' => LANGUAGE_NONE))->fetchField();
|
||||
return path_load(array('pid' => $pid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up a string segment to be used in an URL alias.
|
||||
*
|
||||
* Performs the following possible alterations:
|
||||
* - Remove all HTML tags.
|
||||
* - Process the string through the transliteration module.
|
||||
* - Replace or remove punctuation with the separator character.
|
||||
* - Remove back-slashes.
|
||||
* - Replace non-ascii and non-numeric characters with the separator.
|
||||
* - Remove common words.
|
||||
* - Replace whitespace with the separator character.
|
||||
* - Trim duplicate, leading, and trailing separators.
|
||||
* - Convert to lower-case.
|
||||
* - Shorten to a desired length and logical position based on word boundaries.
|
||||
*
|
||||
* This function should *not* be called on URL alias or path strings because it
|
||||
* is assumed that they are already clean.
|
||||
*
|
||||
* @param $string
|
||||
* A string to clean.
|
||||
* @return
|
||||
* The cleaned string.
|
||||
*/
|
||||
function pathauto_cleanstring($string) {
|
||||
// Use the advanced drupal_static() pattern, since this is called very often.
|
||||
static $drupal_static_fast;
|
||||
if (!isset($drupal_static_fast)) {
|
||||
$drupal_static_fast['cache'] = &drupal_static(__FUNCTION__);
|
||||
}
|
||||
$cache = &$drupal_static_fast['cache'];
|
||||
|
||||
// Generate and cache variables used in this function so that on the second
|
||||
// call to pathauto_cleanstring() we focus on processing.
|
||||
if (!isset($cache)) {
|
||||
$cache = array(
|
||||
'separator' => variable_get('pathauto_separator', '-'),
|
||||
'strings' => array(),
|
||||
'transliterate' => variable_get('pathauto_transliterate', FALSE) && module_exists('transliteration'),
|
||||
'punctuation' => array(),
|
||||
'reduce_ascii' => (bool) variable_get('pathauto_reduce_ascii', FALSE),
|
||||
'ignore_words_regex' => FALSE,
|
||||
'lowercase' => (bool) variable_get('pathauto_case', PATHAUTO_CASE_LOWER),
|
||||
'maxlength' => min(variable_get('pathauto_max_component_length', 100), _pathauto_get_schema_alias_maxlength()),
|
||||
);
|
||||
|
||||
// Generate and cache the punctuation replacements for strtr().
|
||||
$punctuation = pathauto_punctuation_chars();
|
||||
foreach ($punctuation as $name => $details) {
|
||||
$action = variable_get('pathauto_punctuation_' . $name, PATHAUTO_PUNCTUATION_REMOVE);
|
||||
switch ($action) {
|
||||
case PATHAUTO_PUNCTUATION_REMOVE:
|
||||
$cache['punctuation'][$details['value']] = '';
|
||||
break;
|
||||
case PATHAUTO_PUNCTUATION_REPLACE:
|
||||
$cache['punctuation'][$details['value']] = $cache['separator'];
|
||||
break;
|
||||
case PATHAUTO_PUNCTUATION_DO_NOTHING:
|
||||
// Literally do nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate and cache the ignored words regular expression.
|
||||
$ignore_words = variable_get('pathauto_ignore_words', PATHAUTO_IGNORE_WORDS);
|
||||
$ignore_words_regex = preg_replace(array('/^[,\s]+|[,\s]+$/', '/[,\s]+/'), array('', '\b|\b'), $ignore_words);
|
||||
if ($ignore_words_regex) {
|
||||
$cache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b';
|
||||
if (function_exists('mb_eregi_replace')) {
|
||||
$cache['ignore_words_callback'] = 'mb_eregi_replace';
|
||||
}
|
||||
else {
|
||||
$cache['ignore_words_callback'] = 'preg_replace';
|
||||
$cache['ignore_words_regex'] = '/' . $cache['ignore_words_regex'] . '/i';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Empty strings do not need any proccessing.
|
||||
if ($string === '' || $string === NULL) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Check if the string has already been processed, and if so return the
|
||||
// cached result.
|
||||
if (isset($cache['strings'][$string])) {
|
||||
return $cache['strings'][$string];
|
||||
}
|
||||
|
||||
// Remove all HTML tags from the string.
|
||||
$output = strip_tags(decode_entities($string));
|
||||
|
||||
// Optionally transliterate (by running through the Transliteration module)
|
||||
if ($cache['transliterate']) {
|
||||
$output = transliteration_get($output);
|
||||
}
|
||||
|
||||
// Replace or drop punctuation based on user settings
|
||||
$output = strtr($output, $cache['punctuation']);
|
||||
|
||||
// Reduce strings to letters and numbers
|
||||
if ($cache['reduce_ascii']) {
|
||||
$output = preg_replace('/[^a-zA-Z0-9\/]+/', $cache['separator'], $output);
|
||||
}
|
||||
|
||||
// Get rid of words that are on the ignore list
|
||||
if ($cache['ignore_words_regex']) {
|
||||
$words_removed = $cache['ignore_words_callback']($cache['ignore_words_regex'], '', $output);
|
||||
if (drupal_strlen(trim($words_removed)) > 0) {
|
||||
$output = $words_removed;
|
||||
}
|
||||
}
|
||||
|
||||
// Always replace whitespace with the separator.
|
||||
$output = preg_replace('/\s+/', $cache['separator'], $output);
|
||||
|
||||
// Trim duplicates and remove trailing and leading separators.
|
||||
$output = _pathauto_clean_separators($output, $cache['separator']);
|
||||
|
||||
// Optionally convert to lower case.
|
||||
if ($cache['lowercase']) {
|
||||
$output = drupal_strtolower($output);
|
||||
}
|
||||
|
||||
// Shorten to a logical place based on word boundaries.
|
||||
$output = truncate_utf8($output, $cache['maxlength'], TRUE);
|
||||
|
||||
// Cache this result in the static array.
|
||||
$cache['strings'][$string] = $output;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trims duplicate, leading, and trailing separators from a string.
|
||||
*
|
||||
* @param $string
|
||||
* The string to clean path separators from.
|
||||
* @param $separator
|
||||
* The path separator to use when cleaning.
|
||||
* @return
|
||||
* The cleaned version of the string.
|
||||
*
|
||||
* @see pathauto_cleanstring()
|
||||
* @see pathauto_clean_alias()
|
||||
*/
|
||||
function _pathauto_clean_separators($string, $separator = NULL) {
|
||||
static $default_separator;
|
||||
|
||||
if (!isset($separator)) {
|
||||
if (!isset($default_separator)) {
|
||||
$default_separator = variable_get('pathauto_separator', '-');
|
||||
}
|
||||
$separator = $default_separator;
|
||||
}
|
||||
|
||||
$output = $string;
|
||||
|
||||
// Clean duplicate or trailing separators.
|
||||
if (strlen($separator)) {
|
||||
// Escape the separator.
|
||||
$seppattern = preg_quote($separator, '/');
|
||||
|
||||
// Trim any leading or trailing separators.
|
||||
$output = preg_replace("/^$seppattern+|$seppattern+$/", '', $output);
|
||||
|
||||
// Replace trailing separators around slashes.
|
||||
if ($separator !== '/') {
|
||||
$output = preg_replace("/$seppattern+\/|\/$seppattern+/", "/", $output);
|
||||
}
|
||||
|
||||
// Replace multiple separators with a single one.
|
||||
$output = preg_replace("/$seppattern+/", $separator, $output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up an URL alias.
|
||||
*
|
||||
* Performs the following alterations:
|
||||
* - Trim duplicate, leading, and trailing back-slashes.
|
||||
* - Trim duplicate, leading, and trailing separators.
|
||||
* - Shorten to a desired length and logical position based on word boundaries.
|
||||
*
|
||||
* @param $alias
|
||||
* A string with the URL alias to clean up.
|
||||
* @return
|
||||
* The cleaned URL alias.
|
||||
*/
|
||||
function pathauto_clean_alias($alias) {
|
||||
$cache = &drupal_static(__FUNCTION__);
|
||||
|
||||
if (!isset($cache)) {
|
||||
$cache = array(
|
||||
'maxlength' => min(variable_get('pathauto_max_length', 100), _pathauto_get_schema_alias_maxlength()),
|
||||
);
|
||||
}
|
||||
|
||||
$output = $alias;
|
||||
|
||||
// Trim duplicate, leading, and trailing back-slashes.
|
||||
$output = _pathauto_clean_separators($output, '/');
|
||||
|
||||
// Trim duplicate, leading, and trailing separators.
|
||||
$output = _pathauto_clean_separators($output);
|
||||
|
||||
// Shorten to a logical place based on word boundaries.
|
||||
$output = truncate_utf8($output, $cache['maxlength'], TRUE);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply patterns to create an alias.
|
||||
*
|
||||
* @param $module
|
||||
* The name of your module (e.g., 'node').
|
||||
* @param $op
|
||||
* Operation being performed on the content being aliased
|
||||
* ('insert', 'update', 'return', or 'bulkupdate').
|
||||
* @param $source
|
||||
* An internal Drupal path to be aliased.
|
||||
* @param $data
|
||||
* An array of keyed objects to pass to token_replace(). For simple
|
||||
* replacement scenarios 'node', 'user', and others are common keys, with an
|
||||
* accompanying node or user object being the value. Some token types, like
|
||||
* 'site', do not require any explicit information from $data and can be
|
||||
* replaced even if it is empty.
|
||||
* @param $type
|
||||
* For modules which provided pattern items in hook_pathauto(),
|
||||
* the relevant identifier for the specific item to be aliased
|
||||
* (e.g., $node->type).
|
||||
* @param $language
|
||||
* A string specify the path's language.
|
||||
* @return
|
||||
* The alias that was created.
|
||||
*
|
||||
* @see _pathauto_set_alias()
|
||||
* @see token_replace()
|
||||
*/
|
||||
function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $language = LANGUAGE_NONE) {
|
||||
// Retrieve and apply the pattern for this content type.
|
||||
$pattern = pathauto_pattern_load_by_entity($module, $type, $language);
|
||||
if (empty($pattern)) {
|
||||
// No pattern? Do nothing (otherwise we may blow away existing aliases...)
|
||||
return '';
|
||||
}
|
||||
|
||||
// Special handling when updating an item which is already aliased.
|
||||
$existing_alias = NULL;
|
||||
if ($op == 'update' || $op == 'bulkupdate') {
|
||||
if ($existing_alias = _pathauto_existing_alias_data($source, $language)) {
|
||||
switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) {
|
||||
case PATHAUTO_UPDATE_ACTION_NO_NEW:
|
||||
// If an alias already exists, and the update action is set to do nothing,
|
||||
// then gosh-darn it, do nothing.
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace any tokens in the pattern. Uses callback option to clean replacements. No sanitization.
|
||||
$alias = token_replace($pattern, $data, array(
|
||||
'sanitize' => FALSE,
|
||||
'clear' => TRUE,
|
||||
'callback' => 'pathauto_clean_token_values',
|
||||
'language' => (object) array('language' => $language),
|
||||
'pathauto' => TRUE,
|
||||
));
|
||||
|
||||
// Check if the token replacement has not actually replaced any values. If
|
||||
// that is the case, then stop because we should not generate an alias.
|
||||
// @see token_scan()
|
||||
$pattern_tokens_removed = preg_replace('/\[[^\s\]:]*:[^\s\]]*\]/', '', $pattern);
|
||||
if ($alias === $pattern_tokens_removed) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$alias = pathauto_clean_alias($alias);
|
||||
|
||||
// Allow other modules to alter the alias.
|
||||
$context = array(
|
||||
'module' => $module,
|
||||
'op' => $op,
|
||||
'source' => &$source,
|
||||
'data' => $data,
|
||||
'type' => $type,
|
||||
'language' => &$language,
|
||||
'pattern' => $pattern,
|
||||
);
|
||||
drupal_alter('pathauto_alias', $alias, $context);
|
||||
|
||||
// If we have arrived at an empty string, discontinue.
|
||||
if (!drupal_strlen($alias)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// If the alias already exists, generate a new, hopefully unique, variant.
|
||||
$original_alias = $alias;
|
||||
pathauto_alias_uniquify($alias, $source, $language);
|
||||
if ($original_alias != $alias) {
|
||||
// Alert the user why this happened.
|
||||
_pathauto_verbose(t('The automatically generated alias %original_alias conflicted with an existing alias. Alias changed to %alias.', array(
|
||||
'%original_alias' => $original_alias,
|
||||
'%alias' => $alias,
|
||||
)), $op);
|
||||
}
|
||||
|
||||
// Return the generated alias if requested.
|
||||
if ($op == 'return') {
|
||||
return $alias;
|
||||
}
|
||||
|
||||
// Build the new path alias array and send it off to be created.
|
||||
$path = array(
|
||||
'source' => $source,
|
||||
'alias' => $alias,
|
||||
'language' => $language,
|
||||
);
|
||||
$path = _pathauto_set_alias($path, $existing_alias, $op);
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to ensure a path alias is unique and add suffix variants if necessary.
|
||||
*
|
||||
* Given an alias 'content/test' if a path alias with the exact alias already
|
||||
* exists, the function will change the alias to 'content/test-0' and will
|
||||
* increase the number suffix until it finds a unique alias.
|
||||
*
|
||||
* @param $alias
|
||||
* A string with the alias. Can be altered by reference.
|
||||
* @param $source
|
||||
* A string with the path source.
|
||||
* @param $langcode
|
||||
* A string with a language code.
|
||||
*/
|
||||
function pathauto_alias_uniquify(&$alias, $source, $langcode) {
|
||||
if (!_pathauto_alias_exists($alias, $source, $langcode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the alias already exists, generate a new, hopefully unique, variant
|
||||
$maxlength = min(variable_get('pathauto_max_length', 100), _pathauto_get_schema_alias_maxlength());
|
||||
$separator = variable_get('pathauto_separator', '-');
|
||||
$original_alias = $alias;
|
||||
|
||||
$i = 0;
|
||||
do {
|
||||
// Append an incrementing numeric suffix until we find a unique alias.
|
||||
$unique_suffix = $separator . $i;
|
||||
$alias = truncate_utf8($original_alias, $maxlength - drupal_strlen($unique_suffix, TRUE)) . $unique_suffix;
|
||||
$i++;
|
||||
} while (_pathauto_alias_exists($alias, $source, $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the given path is a valid menu callback.
|
||||
*
|
||||
* Taken from menu_execute_active_handler().
|
||||
*
|
||||
* @param $path
|
||||
* A string containing a relative path.
|
||||
* @return
|
||||
* TRUE if the path already exists.
|
||||
*/
|
||||
function _pathauto_path_is_callback($path) {
|
||||
// We need to use a try/catch here because of a core bug which will throw an
|
||||
// exception if $path is something like 'node/foo/bar'.
|
||||
// @todo Remove when http://drupal.org/node/1302158 is fixed in core.
|
||||
try {
|
||||
$menu = menu_get_item($path);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (isset($menu['path']) && $menu['path'] == $path) {
|
||||
return TRUE;
|
||||
}
|
||||
elseif (is_file(DRUPAL_ROOT . '/' . $path) || is_dir(DRUPAL_ROOT . '/' . $path)) {
|
||||
// Do not allow existing files or directories to get assigned an automatic
|
||||
// alias. Note that we do not need to use is_link() to check for symbolic
|
||||
// links since this returns TRUE for either is_file() or is_dir() already.
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private function for Pathauto to create an alias.
|
||||
*
|
||||
* @param $path
|
||||
* An associative array containing the following keys:
|
||||
* - source: The internal system path.
|
||||
* - alias: The URL alias.
|
||||
* - pid: (optional) Unique path alias identifier.
|
||||
* - language: (optional) The language of the alias.
|
||||
* @param $existing_alias
|
||||
* (optional) An associative array of the existing path alias.
|
||||
* @param $op
|
||||
* An optional string with the operation being performed.
|
||||
*
|
||||
* @return
|
||||
* The saved path from path_save() or NULL if the path was not saved.
|
||||
*
|
||||
* @see path_save()
|
||||
*/
|
||||
function _pathauto_set_alias(array $path, $existing_alias = NULL, $op = NULL) {
|
||||
$verbose = _pathauto_verbose(NULL, $op);
|
||||
|
||||
// Alert users that an existing callback cannot be overridden automatically
|
||||
if (_pathauto_path_is_callback($path['alias'])) {
|
||||
if ($verbose) {
|
||||
_pathauto_verbose(t('Ignoring alias %alias due to existing path conflict.', array('%alias' => $path['alias'])));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Alert users if they are trying to create an alias that is the same as the internal path
|
||||
if ($path['source'] == $path['alias']) {
|
||||
if ($verbose) {
|
||||
_pathauto_verbose(t('Ignoring alias %alias because it is the same as the internal path.', array('%alias' => $path['alias'])));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip replacing the current alias with an identical alias
|
||||
if (empty($existing_alias) || $existing_alias['alias'] != $path['alias']) {
|
||||
$path += array('pathauto' => TRUE, 'original' => $existing_alias);
|
||||
|
||||
// If there is already an alias, respect some update actions.
|
||||
if (!empty($existing_alias)) {
|
||||
switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) {
|
||||
case PATHAUTO_UPDATE_ACTION_NO_NEW:
|
||||
// Do not create the alias.
|
||||
return;
|
||||
case PATHAUTO_UPDATE_ACTION_LEAVE:
|
||||
// Create a new alias instead of overwriting the existing by leaving
|
||||
// $path['pid'] empty.
|
||||
break;
|
||||
case PATHAUTO_UPDATE_ACTION_DELETE:
|
||||
// The delete actions should overwrite the existing alias.
|
||||
$path['pid'] = $existing_alias['pid'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the path array.
|
||||
path_save($path);
|
||||
|
||||
if ($verbose) {
|
||||
if (!empty($existing_alias['pid'])) {
|
||||
_pathauto_verbose(t('Created new alias %alias for %source, replacing %old_alias.', array('%alias' => $path['alias'], '%source' => $path['source'], '%old_alias' => $existing_alias['alias'])));
|
||||
}
|
||||
else {
|
||||
_pathauto_verbose(t('Created new alias %alias for %source.', array('%alias' => $path['alias'], '%source' => $path['source'])));
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a helpful message if verbose output is enabled.
|
||||
*
|
||||
* Verbose output is only enabled when:
|
||||
* - The 'pathauto_verbose' setting is enabled.
|
||||
* - The current user has the 'notify of path changes' permission.
|
||||
* - The $op parameter is anything but 'bulkupdate' or 'return'.
|
||||
*
|
||||
* @param $message
|
||||
* An optional string of the verbose message to display. This string should
|
||||
* already be run through t().
|
||||
* @param $op
|
||||
* An optional string with the operation being performed.
|
||||
* @return
|
||||
* TRUE if verbose output is enabled, or FALSE otherwise.
|
||||
*/
|
||||
function _pathauto_verbose($message = NULL, $op = NULL) {
|
||||
static $verbose;
|
||||
|
||||
if (!isset($verbose)) {
|
||||
$verbose = variable_get('pathauto_verbose', FALSE) && user_access('notify of path changes');
|
||||
}
|
||||
|
||||
if (!$verbose || (isset($op) && in_array($op, array('bulkupdate', 'return')))) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ($message) {
|
||||
drupal_set_message($message);
|
||||
}
|
||||
|
||||
return $verbose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean tokens so they are URL friendly.
|
||||
*
|
||||
* @param $replacements
|
||||
* An array of token replacements that need to be "cleaned" for use in the URL.
|
||||
* @param $data
|
||||
* An array of objects used to generate the replacements.
|
||||
* @param $options
|
||||
* An array of options used to generate the replacements.
|
||||
*/
|
||||
function pathauto_clean_token_values(&$replacements, $data = array(), $options = array()) {
|
||||
foreach ($replacements as $token => $value) {
|
||||
// Only clean non-path tokens.
|
||||
if (!preg_match('/(path|alias|url|url-brief)\]$/', $token)) {
|
||||
$replacements[$token] = pathauto_cleanstring($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of arrays for punctuation values.
|
||||
*
|
||||
* Returns an array of arrays for punctuation values keyed by a name, including
|
||||
* the value and a textual description.
|
||||
* Can and should be expanded to include "all" non text punctuation values.
|
||||
*
|
||||
* @return
|
||||
* An array of arrays for punctuation values keyed by a name, including the
|
||||
* value and a textual description.
|
||||
*/
|
||||
function pathauto_punctuation_chars() {
|
||||
$punctuation = &drupal_static(__FUNCTION__);
|
||||
|
||||
if (!isset($punctuation)) {
|
||||
$cid = 'pathauto:punctuation:' . $GLOBALS['language']->language;
|
||||
if ($cache = cache_get($cid)) {
|
||||
$punctuation = $cache->data;
|
||||
}
|
||||
else {
|
||||
$punctuation = array();
|
||||
$punctuation['double_quotes'] = array('value' => '"', 'name' => t('Double quotation marks'));
|
||||
$punctuation['quotes'] = array('value' => '\'', 'name' => t("Single quotation marks (apostrophe)"));
|
||||
$punctuation['backtick'] = array('value' => '`', 'name' => t('Back tick'));
|
||||
$punctuation['comma'] = array('value' => ',', 'name' => t('Comma'));
|
||||
$punctuation['period'] = array('value' => '.', 'name' => t('Period'));
|
||||
$punctuation['hyphen'] = array('value' => '-', 'name' => t('Hyphen'));
|
||||
$punctuation['underscore'] = array('value' => '_', 'name' => t('Underscore'));
|
||||
$punctuation['colon'] = array('value' => ':', 'name' => t('Colon'));
|
||||
$punctuation['semicolon'] = array('value' => ';', 'name' => t('Semicolon'));
|
||||
$punctuation['pipe'] = array('value' => '|', 'name' => t('Vertical bar (pipe)'));
|
||||
$punctuation['left_curly'] = array('value' => '{', 'name' => t('Left curly bracket'));
|
||||
$punctuation['left_square'] = array('value' => '[', 'name' => t('Left square bracket'));
|
||||
$punctuation['right_curly'] = array('value' => '}', 'name' => t('Right curly bracket'));
|
||||
$punctuation['right_square'] = array('value' => ']', 'name' => t('Right square bracket'));
|
||||
$punctuation['plus'] = array('value' => '+', 'name' => t('Plus sign'));
|
||||
$punctuation['equal'] = array('value' => '=', 'name' => t('Equal sign'));
|
||||
$punctuation['asterisk'] = array('value' => '*', 'name' => t('Asterisk'));
|
||||
$punctuation['ampersand'] = array('value' => '&', 'name' => t('Ampersand'));
|
||||
$punctuation['percent'] = array('value' => '%', 'name' => t('Percent sign'));
|
||||
$punctuation['caret'] = array('value' => '^', 'name' => t('Caret'));
|
||||
$punctuation['dollar'] = array('value' => '$', 'name' => t('Dollar sign'));
|
||||
$punctuation['hash'] = array('value' => '#', 'name' => t('Number sign (pound sign, hash)'));
|
||||
$punctuation['at'] = array('value' => '@', 'name' => t('At sign'));
|
||||
$punctuation['exclamation'] = array('value' => '!', 'name' => t('Exclamation mark'));
|
||||
$punctuation['tilde'] = array('value' => '~', 'name' => t('Tilde'));
|
||||
$punctuation['left_parenthesis'] = array('value' => '(', 'name' => t('Left parenthesis'));
|
||||
$punctuation['right_parenthesis'] = array('value' => ')', 'name' => t('Right parenthesis'));
|
||||
$punctuation['question_mark'] = array('value' => '?', 'name' => t('Question mark'));
|
||||
$punctuation['less_than'] = array('value' => '<', 'name' => t('Less-than sign'));
|
||||
$punctuation['greater_than'] = array('value' => '>', 'name' => t('Greater-than sign'));
|
||||
$punctuation['slash'] = array('value' => '/', 'name' => t('Slash'));
|
||||
$punctuation['back_slash'] = array('value' => '\\', 'name' => t('Backslash'));
|
||||
|
||||
// Allow modules to alter the punctuation list and cache the result.
|
||||
drupal_alter('pathauto_punctuation_chars', $punctuation);
|
||||
cache_set($cid, $punctuation);
|
||||
}
|
||||
}
|
||||
|
||||
return $punctuation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the maximum length of the {url_alias}.alias field from the schema.
|
||||
*
|
||||
* @return
|
||||
* An integer of the maximum URL alias length allowed by the database.
|
||||
*/
|
||||
function _pathauto_get_schema_alias_maxlength() {
|
||||
$maxlength = &drupal_static(__FUNCTION__);
|
||||
if (!isset($maxlength)) {
|
||||
$schema = drupal_get_schema('url_alias');
|
||||
$maxlength = $schema['fields']['alias']['length'];
|
||||
}
|
||||
return $maxlength;
|
||||
}
|
15
sites/all/modules/contrib/content/pathauto/pathauto.info
Normal file
15
sites/all/modules/contrib/content/pathauto/pathauto.info
Normal file
@@ -0,0 +1,15 @@
|
||||
name = Pathauto
|
||||
description = Provides a mechanism for modules to automatically generate aliases for the content they manage.
|
||||
dependencies[] = path
|
||||
dependencies[] = token
|
||||
core = 7.x
|
||||
files[] = pathauto.test
|
||||
configure = admin/config/search/path/patterns
|
||||
recommends[] = redirect
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-08-09
|
||||
version = "7.x-1.2"
|
||||
core = "7.x"
|
||||
project = "pathauto"
|
||||
datestamp = "1344525185"
|
||||
|
182
sites/all/modules/contrib/content/pathauto/pathauto.install
Normal file
182
sites/all/modules/contrib/content/pathauto/pathauto.install
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update, and uninstall functions for Pathauto.
|
||||
*
|
||||
* @ingroup pathauto
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function pathauto_install() {
|
||||
// Set some default variables necessary for the module to perform.
|
||||
variable_set('pathauto_node_pattern', 'content/[node:title]');
|
||||
variable_set('pathauto_taxonomy_term_pattern', '[term:vocabulary]/[term:name]');
|
||||
variable_set('pathauto_forum_pattern', '[term:vocabulary]/[term:name]');
|
||||
variable_set('pathauto_user_pattern', 'users/[user:name]');
|
||||
variable_set('pathauto_blog_pattern', 'blogs/[user:name]');
|
||||
|
||||
// Set the default separator character to replace instead of remove (default).
|
||||
variable_set('pathauto_punctuation_hyphen', 1);
|
||||
|
||||
// Set the weight to 1
|
||||
db_update('system')
|
||||
->fields(array('weight' => 1))
|
||||
->condition('type', 'module')
|
||||
->condition('name', 'pathauto')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function pathauto_uninstall() {
|
||||
// Delete all the pathauto variables and then clear the variable cache.
|
||||
db_query("DELETE FROM {variable} WHERE name LIKE 'pathauto_%'");
|
||||
cache_clear_all('variables', 'cache');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the unsupported user/%/contact and user/%/tracker pattern variables.
|
||||
*/
|
||||
function pathauto_update_6200() {
|
||||
variable_del('pathauto_contact_bulkupdate');
|
||||
variable_del('pathauto_contact_pattern');
|
||||
variable_del('pathauto_contact_supportsfeeds');
|
||||
variable_del('pathauto_contact_applytofeeds');
|
||||
variable_del('pathauto_tracker_bulkupdate');
|
||||
variable_del('pathauto_tracker_pattern');
|
||||
variable_del('pathauto_tracker_supportsfeeds');
|
||||
variable_del('pathauto_tracker_applytofeeds');
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty update since it is handled by pathauto_update_7000().
|
||||
*/
|
||||
function pathauto_update_6201() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty update since it is handled by pathauto_update_7004().
|
||||
*/
|
||||
function pathauto_update_6202() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove obsolete variables since batch API is now used.
|
||||
*/
|
||||
function pathauto_update_7000() {
|
||||
variable_del('pathauto_max_bulk_update');
|
||||
variable_del('pathauto_node_bulkupdate');
|
||||
variable_del('pathauto_taxonomy_bulkupdate');
|
||||
variable_del('pathauto_forum_bulkupdate');
|
||||
variable_del('pathauto_user_bulkupdate');
|
||||
variable_del('pathauto_blog_bulkupdate');
|
||||
variable_del('pathauto_modulelist');
|
||||
variable_del('pathauto_indexaliases');
|
||||
variable_del('pathauto_indexaliases_bulkupdate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty update since feed paths are no longer supported.
|
||||
*/
|
||||
function pathauto_update_7001() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update pathauto_taxonomy_[vid]_pattern variables to pathauto_taxonomy_[machinename]_pattern.
|
||||
*/
|
||||
function pathauto_update_7002() {
|
||||
if (module_exists('taxonomy')) {
|
||||
$vocabularies = taxonomy_get_vocabularies();
|
||||
foreach ($vocabularies as $vid => $vocabulary) {
|
||||
if ($vid == variable_get('forum_nav_vocabulary', '')) {
|
||||
// Skip the forum vocabulary.
|
||||
continue;
|
||||
}
|
||||
if ($pattern = variable_get('pathauto_taxonomy_' . $vid . '_pattern', '')) {
|
||||
variable_set('pathauto_taxonomy_' . $vocabulary->machine_name . '_pattern', $pattern);
|
||||
}
|
||||
variable_del('pathauto_taxonomy_' . $vid . '_pattern');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename 'taxonomy' variables to use the entity type 'taxonomy_term'.
|
||||
*/
|
||||
function pathauto_update_7003() {
|
||||
$variables = db_select('variable', 'v')
|
||||
->fields('v', array('name'))
|
||||
->condition(db_and()
|
||||
->condition('name', db_like("pathauto_taxonomy_") . '%', 'LIKE')
|
||||
->condition('name', db_like("pathauto_taxonomy_term_") . '%', 'NOT LIKE')
|
||||
)
|
||||
->execute()
|
||||
->fetchCol();
|
||||
foreach ($variables as $variable) {
|
||||
$value = variable_get($variable);
|
||||
variable_del($variable);
|
||||
$variable = strtr($variable, array('pathauto_taxonomy_' => 'pathauto_taxonomy_term_'));
|
||||
variable_set($variable, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove obsolete variables for removed feed handling.
|
||||
*/
|
||||
function pathauto_update_7004() {
|
||||
variable_del('pathauto_node_supportsfeeds');
|
||||
variable_del('pathauto_node_applytofeeds');
|
||||
variable_del('pathauto_taxonomy_supportsfeeds');
|
||||
variable_del('pathauto_taxonomy_applytofeeds');
|
||||
variable_del('pathauto_forum_supportsfeeds');
|
||||
variable_del('pathauto_forum_applytofeeds');
|
||||
variable_del('pathauto_user_supportsfeeds');
|
||||
variable_del('pathauto_user_applytofeeds');
|
||||
variable_del('pathauto_blog_supportsfeeds');
|
||||
variable_del('pathauto_blog_applytofeeds');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix original incorrect tokens in taxonomy and forum patterns.
|
||||
*/
|
||||
function pathauto_update_7005() {
|
||||
$replacements = array(
|
||||
'[vocabulary:name]' => '[term:vocabulary]',
|
||||
'[vocabulary:' => '[term:vocabulary:',
|
||||
'[term:catpath]' => '[term:name]',
|
||||
'[term:path]' => '[term:name]',
|
||||
);
|
||||
$variables = db_select('variable', 'v')
|
||||
->fields('v', array('name'))
|
||||
->condition(db_or()
|
||||
->condition('name', db_like("pathauto_taxonomy_term_") . '%' . db_like('pattern'), 'LIKE')
|
||||
->condition('name', db_like("pathauto_forum_") . '%' . db_like('pattern'), 'LIKE')
|
||||
)
|
||||
->execute()
|
||||
->fetchCol();
|
||||
foreach ($variables as $variable) {
|
||||
if ($pattern = variable_get($variable)) {
|
||||
$pattern = strtr($pattern, $replacements);
|
||||
variable_set($variable, $pattern);
|
||||
}
|
||||
}
|
||||
|
||||
return 'Your Pathauto taxonomy and forum patterns have been corrected. You may wish to regenerate your taxonomy and forum term URL aliases.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of Drupal 6 tokens and their Drupal 7 token names.
|
||||
*/
|
||||
function _pathauto_upgrade_token_list() {
|
||||
$tokens = array(
|
||||
//'catpath' => 'node:term-lowest:parent:path][node:term-lowest',
|
||||
//'catalias' => 'node:term-lowest:path',
|
||||
//'termpath' => 'term:parent:path][term:name',
|
||||
//'termalias' => 'term:url:alias',
|
||||
//'bookpathalias' => 'node:book:parent:path',
|
||||
);
|
||||
}
|
22
sites/all/modules/contrib/content/pathauto/pathauto.js
Normal file
22
sites/all/modules/contrib/content/pathauto/pathauto.js
Normal file
@@ -0,0 +1,22 @@
|
||||
(function ($) {
|
||||
|
||||
Drupal.behaviors.pathFieldsetSummaries = {
|
||||
attach: function (context) {
|
||||
$('fieldset.path-form', context).drupalSetSummary(function (context) {
|
||||
var path = $('.form-item-path-alias input').val();
|
||||
var automatic = $('.form-item-path-pathauto input').attr('checked');
|
||||
|
||||
if (automatic) {
|
||||
return Drupal.t('Automatic alias');
|
||||
}
|
||||
if (path) {
|
||||
return Drupal.t('Alias: @alias', { '@alias': path });
|
||||
}
|
||||
else {
|
||||
return Drupal.t('No alias');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
840
sites/all/modules/contrib/content/pathauto/pathauto.module
Normal file
840
sites/all/modules/contrib/content/pathauto/pathauto.module
Normal file
@@ -0,0 +1,840 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup pathauto Pathauto: Automatically generates aliases for content
|
||||
*
|
||||
* The Pathauto module automatically generates path aliases for various kinds of
|
||||
* content (nodes, categories, users) without requiring the user to manually
|
||||
* specify the path alias. This allows you to get aliases like
|
||||
* /category/my-node-title.html instead of /node/123. The aliases are based upon
|
||||
* a "pattern" system which the administrator can control.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main file for the Pathauto module, which automatically generates aliases for content.
|
||||
*
|
||||
* @ingroup pathauto
|
||||
*/
|
||||
|
||||
/**
|
||||
* The default ignore word list.
|
||||
*/
|
||||
define('PATHAUTO_IGNORE_WORDS', 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with');
|
||||
|
||||
/**
|
||||
* Implements hook_hook_info().
|
||||
*/
|
||||
function pathauto_hook_info() {
|
||||
$info['pathauto'] = array('group' => 'pathauto');
|
||||
$info['path_alias_types'] = array('group' => 'pathauto');
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_module_implements_alter().
|
||||
*
|
||||
* Adds pathauto support for core modules.
|
||||
*/
|
||||
function pathauto_module_implements_alter(&$implementations, $hook) {
|
||||
$hooks = pathauto_hook_info();
|
||||
if (isset($hooks[$hook])) {
|
||||
$modules = array('node', 'taxonomy', 'user', 'forum', 'blog');
|
||||
foreach ($modules as $module) {
|
||||
if (module_exists($module)) {
|
||||
$implementations[$module] = TRUE;
|
||||
}
|
||||
}
|
||||
// Move pathauto.module to get included first since it is responsible for
|
||||
// other modules.
|
||||
unset($implementations['pathauto']);
|
||||
$implementations = array_merge(array('pathauto' => 'pathauto'), $implementations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function pathauto_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/help#pathauto':
|
||||
module_load_include('inc', 'pathauto');
|
||||
$output = '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('Provides a mechanism for modules to automatically generate aliases for the content they manage.') . '</p>';
|
||||
$output .= '<h3>' . t('Settings') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Maximum alias and component length') . '</dt>';
|
||||
$output .= '<dd>' . t('The <strong>maximum alias length</strong> and <strong>maximum component length</strong> values default to 100 and have a limit of @max from Pathauto. This length is limited by the length of the "alias" column of the url_alias database table. The default database schema for this column is @max. If you set a length that is equal to that of the one set in the "alias" column it will cause problems in situations where the system needs to append additional words to the aliased URL. You should enter a value that is the length of the "alias" column minus the length of any strings that might get added to the end of the URL. The length of strings that might get added to the end of your URLs depends on which modules you have enabled and on your Pathauto settings. The recommended and default value is 100.', array('@max' => _pathauto_get_schema_alias_maxlength())) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function pathauto_permission() {
|
||||
return array(
|
||||
'administer pathauto' => array(
|
||||
'title' => t('Administer pathauto'),
|
||||
'description' => t('Allows a user to configure patterns for automated aliases and bulk delete URL-aliases.'),
|
||||
),
|
||||
'notify of path changes' => array(
|
||||
'title' => t('Notify of Path Changes'),
|
||||
'description' => t('Determines whether or not users are notified.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function pathauto_menu() {
|
||||
$items['admin/config/search/path/patterns'] = array(
|
||||
'title' => 'Patterns',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('pathauto_patterns_form'),
|
||||
'access arguments' => array('administer pathauto'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 10,
|
||||
'file' => 'pathauto.admin.inc',
|
||||
);
|
||||
$items['admin/config/search/path/settings'] = array(
|
||||
'title' => 'Settings',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('pathauto_settings_form'),
|
||||
'access arguments' => array('administer pathauto'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 20,
|
||||
'file' => 'pathauto.admin.inc',
|
||||
);
|
||||
$items['admin/config/search/path/update_bulk'] = array(
|
||||
'title' => 'Bulk update',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('pathauto_bulk_update_form'),
|
||||
'access arguments' => array('administer url aliases'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 30,
|
||||
'file' => 'pathauto.admin.inc',
|
||||
);
|
||||
$items['admin/config/search/path/delete_bulk'] = array(
|
||||
'title' => 'Delete aliases',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('pathauto_admin_delete'),
|
||||
'access arguments' => array('administer url aliases'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'weight' => 40,
|
||||
'file' => 'pathauto.admin.inc',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an URL alias pattern by entity, bundle, and language.
|
||||
*
|
||||
* @param $entity
|
||||
* An entity (e.g. node, taxonomy, user, etc.)
|
||||
* @param $bundle
|
||||
* A bundle (e.g. content type, vocabulary ID, etc.)
|
||||
* @param $language
|
||||
* A language code, defaults to the LANGUAGE_NONE constant.
|
||||
*/
|
||||
function pathauto_pattern_load_by_entity($entity, $bundle = '', $language = LANGUAGE_NONE) {
|
||||
$patterns = &drupal_static(__FUNCTION__, array());
|
||||
|
||||
$pattern_id = "$entity:$bundle:$language";
|
||||
if (!isset($patterns[$pattern_id])) {
|
||||
$variables = array();
|
||||
if ($language != LANGUAGE_NONE) {
|
||||
$variables[] = "pathauto_{$entity}_{$bundle}_{$language}_pattern";
|
||||
}
|
||||
if ($bundle) {
|
||||
$variables[] = "pathauto_{$entity}_{$bundle}_pattern";
|
||||
}
|
||||
$variables[] = "pathauto_{$entity}_pattern";
|
||||
|
||||
foreach ($variables as $variable) {
|
||||
if ($pattern = trim(variable_get($variable, ''))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$patterns[$pattern_id] = $pattern;
|
||||
}
|
||||
|
||||
return $patterns[$pattern_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple URL aliases.
|
||||
*
|
||||
* Intent of this is to abstract a potential path_delete_multiple() function
|
||||
* for Drupal 7 or 8.
|
||||
*
|
||||
* @param $pids
|
||||
* An array of path IDs to delete.
|
||||
*/
|
||||
function pathauto_path_delete_multiple($pids) {
|
||||
foreach ($pids as $pid) {
|
||||
path_delete(array('pid' => $pid));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an URL alias and any of its sub-paths.
|
||||
*
|
||||
* Given a source like 'node/1' this function will delete any alias that have
|
||||
* that specific source or any sources that match 'node/1/%'.
|
||||
*
|
||||
* @param $source
|
||||
* An string with a source URL path.
|
||||
*/
|
||||
function pathauto_path_delete_all($source) {
|
||||
$sql = "SELECT pid FROM {url_alias} WHERE source = :source OR source LIKE :source_wildcard";
|
||||
$pids = db_query($sql, array(':source' => $source, ':source_wildcard' => $source . '/%'))->fetchCol();
|
||||
if ($pids) {
|
||||
pathauto_path_delete_multiple($pids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an entity URL alias and any of its sub-paths.
|
||||
*
|
||||
* This function also checks to see if the default entity URI is different from
|
||||
* the current entity URI and will delete any of the default aliases.
|
||||
*
|
||||
* @param $entity_type
|
||||
* A string with the entity type.
|
||||
* @param $entity
|
||||
* An entity object.
|
||||
* @param $default_uri
|
||||
* The optional default uri path for the entity.
|
||||
*/
|
||||
function pathauto_entity_path_delete_all($entity_type, $entity, $default_uri = NULL) {
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
pathauto_path_delete_all($uri['path']);
|
||||
if (isset($default_uri) && $uri['path'] != $default_uri) {
|
||||
pathauto_path_delete_all($default_uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_rename_bundle().
|
||||
*
|
||||
* Respond to machine name changes for pattern variables.
|
||||
*/
|
||||
function pathauto_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
|
||||
$variables = db_select('variable', 'v')
|
||||
->fields('v', array('name'))
|
||||
->condition('name', db_like("pathauto_{$entity_type}_{$bundle_old}_") . '%', 'LIKE')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
foreach ($variables as $variable) {
|
||||
$value = variable_get($variable, '');
|
||||
variable_del($variable);
|
||||
$variable = strtr($variable, array("{$entity_type}_{$bundle_old}" => "{$entity_type}_{$bundle_new}"));
|
||||
variable_set($variable, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_delete_bundle().
|
||||
*
|
||||
* Respond to sub-types being deleted, their patterns can be removed.
|
||||
*/
|
||||
function pathauto_field_attach_delete_bundle($entity_type, $bundle) {
|
||||
$variables = db_select('variable', 'v')
|
||||
->fields('v', array('name'))
|
||||
->condition('name', db_like("pathauto_{$entity_type}_{$bundle}_") . '%', 'LIKE')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
foreach ($variables as $variable) {
|
||||
variable_del($variable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_form().
|
||||
*
|
||||
* Add the automatic alias form elements to an existing path form fieldset.
|
||||
*/
|
||||
function pathauto_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
|
||||
list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
if (!isset($form['path'])) {
|
||||
// This entity must be supported by core's path.module first.
|
||||
// @todo Investigate removing this and supporting all fieldable entities.
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Taxonomy terms do not have an actual fieldset for path settings.
|
||||
// Merge in the defaults.
|
||||
$form['path'] += array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('URL path settings'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => empty($form['path']['alias']),
|
||||
'#group' => 'additional_settings',
|
||||
'#attributes' => array(
|
||||
'class' => array('path-form'),
|
||||
),
|
||||
'#access' => user_access('create url aliases') || user_access('administer url aliases'),
|
||||
'#weight' => 30,
|
||||
'#tree' => TRUE,
|
||||
'#element_validate' => array('path_form_element_validate'),
|
||||
);
|
||||
}
|
||||
|
||||
$pattern = pathauto_pattern_load_by_entity($entity_type, $bundle, $langcode);
|
||||
if (empty($pattern)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($entity->path['pathauto'])) {
|
||||
if (!empty($id)) {
|
||||
module_load_include('inc', 'pathauto');
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
$path = drupal_get_path_alias($uri['path'], $langcode);
|
||||
$pathauto_alias = pathauto_create_alias($entity_type, 'return', $uri['path'], array($entity_type => $entity), $bundle, $langcode);
|
||||
$entity->path['pathauto'] = ($path != $uri['path'] && $path == $pathauto_alias);
|
||||
}
|
||||
else {
|
||||
$entity->path['pathauto'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Add JavaScript that will disable the path textfield when the automatic
|
||||
// alias checkbox is checked.
|
||||
$form['path']['alias']['#states']['!enabled']['input[name="path[pathauto]"]'] = array('checked' => TRUE);
|
||||
|
||||
// Override path.module's vertical tabs summary.
|
||||
$form['path']['#attached']['js'] = array(
|
||||
'vertical-tabs' => drupal_get_path('module', 'pathauto') . '/pathauto.js'
|
||||
);
|
||||
|
||||
$form['path']['pathauto'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Generate automatic URL alias'),
|
||||
'#default_value' => $entity->path['pathauto'],
|
||||
'#description' => t('Uncheck this to create a custom alias below.'),
|
||||
'#weight' => -1,
|
||||
);
|
||||
|
||||
// Add a shortcut link to configure URL alias patterns.
|
||||
if (drupal_valid_path('admin/config/search/path/patterns')) {
|
||||
$form['path']['pathauto']['#description'] .= ' ' . l(t('Configure URL alias patterns.'), 'admin/config/search/path/patterns');
|
||||
}
|
||||
|
||||
if ($entity->path['pathauto'] && !empty($entity->old_alias) && empty($entity->path['alias'])) {
|
||||
$form['path']['alias']['#default_value'] = $entity->old_alias;
|
||||
$entity->path['alias'] = $entity->old_alias;
|
||||
}
|
||||
|
||||
// For Pathauto to remember the old alias and prevent the Path module from
|
||||
// deleting it when Pathauto wants to preserve it.
|
||||
if (!empty($entity->path['alias'])) {
|
||||
$form['path']['old_alias'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $entity->path['alias'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_presave().
|
||||
*/
|
||||
function pathauto_entity_presave($entity, $type) {
|
||||
// About to be saved (before insert/update)
|
||||
if (!empty($entity->path['pathauto']) && isset($entity->path['old_alias'])
|
||||
&& $entity->path['alias'] == '' && $entity->path['old_alias'] != '') {
|
||||
/**
|
||||
* There was an old alias, but when pathauto_perform_alias was checked
|
||||
* the javascript disabled the textbox which led to an empty value being
|
||||
* submitted. Restoring the old path-value here prevents the Path module
|
||||
* from deleting any old alias before Pathauto gets control.
|
||||
*/
|
||||
$entity->path['alias'] = $entity->path['old_alias'];
|
||||
}
|
||||
|
||||
// Help prevent errors with progromatically creating entities by defining
|
||||
// path['alias'] as an empty string.
|
||||
// @see http://drupal.org/node/1328180
|
||||
// @see http://drupal.org/node/1576552
|
||||
if (isset($entity->path['pathauto']) && !isset($entity->path['alias'])) {
|
||||
$entity->path['alias'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function pathauto_action_info() {
|
||||
$info['pathauto_node_update_action'] = array(
|
||||
'type' => 'node',
|
||||
'label' => t('Update node alias'),
|
||||
'configurable' => FALSE,
|
||||
);
|
||||
$info['pathauto_taxonomy_term_update_action'] = array(
|
||||
'type' => 'taxonomy_term',
|
||||
'label' => t('Update taxonomy term alias'),
|
||||
'configurable' => FALSE,
|
||||
);
|
||||
$info['pathauto_user_update_action'] = array(
|
||||
'type' => 'user',
|
||||
'label' => t('Update user alias'),
|
||||
'configurable' => FALSE,
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language code of the given entity.
|
||||
*
|
||||
* Backward compatibility layer to ensure that installations running an older
|
||||
* version of core where entity_language() is not avilable do not break.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* An entity type.
|
||||
* @param object $entity
|
||||
* An entity object.
|
||||
* @param bool $check_language_property
|
||||
* A boolean if TRUE, will attempt to fetch the language code from
|
||||
* $entity->language if the entity_language() function failed or does not
|
||||
* exist. Default is TRUE.
|
||||
*/
|
||||
function pathauto_entity_language($entity_type, $entity, $check_language_property = TRUE) {
|
||||
$langcode = NULL;
|
||||
|
||||
if (function_exists('entity_language')) {
|
||||
$langcode = entity_language($entity_type, $entity);
|
||||
}
|
||||
elseif ($check_language_property && !empty($entity->language)) {
|
||||
$langcode = $entity->language;
|
||||
}
|
||||
|
||||
return !empty($langcode) ? $langcode : LANGUAGE_NONE;
|
||||
}
|
||||
|
||||
if (!function_exists('path_field_extra_fields')) {
|
||||
/**
|
||||
* Implements hook_field_extra_fields() on behalf of path.module.
|
||||
*
|
||||
* Add support for the 'URL path settings' to be re-ordered by the user on the
|
||||
* 'Manage Fields' tab of content types and vocabularies.
|
||||
*/
|
||||
function path_field_extra_fields() {
|
||||
$info = array();
|
||||
|
||||
foreach (node_type_get_types() as $node_type) {
|
||||
if (!isset($info['node'][$node_type->type]['form']['path'])) {
|
||||
$info['node'][$node_type->type]['form']['path'] = array(
|
||||
'label' => t('URL path settings'),
|
||||
'description' => t('Path module form elements'),
|
||||
'weight' => 30,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (module_exists('taxonomy')) {
|
||||
$vocabularies = taxonomy_get_vocabularies();
|
||||
foreach ($vocabularies as $vocabulary) {
|
||||
if (!isset($info['taxonomy_term'][$vocabulary->machine_name]['form']['path'])) {
|
||||
$info['taxonomy_term'][$vocabulary->machine_name]['form']['path'] = array(
|
||||
'label' => t('URL path settings'),
|
||||
'description' => t('Path module form elements'),
|
||||
'weight' => 30,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name pathauto_node Pathauto integration for the core node module.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_node_insert().
|
||||
*/
|
||||
function pathauto_node_insert($node) {
|
||||
// @todo Remove the next line when http://drupal.org/node/1025870 is fixed.
|
||||
unset($node->uri);
|
||||
pathauto_node_update_alias($node, 'insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_update().
|
||||
*/
|
||||
function pathauto_node_update($node) {
|
||||
pathauto_node_update_alias($node, 'update');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_delete().
|
||||
*/
|
||||
function pathauto_node_delete($node) {
|
||||
pathauto_entity_path_delete_all('node', $node, "node/{$node->nid}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter().
|
||||
*
|
||||
* Add the Pathauto settings to the node form.
|
||||
*/
|
||||
function pathauto_form_node_form_alter(&$form, &$form_state) {
|
||||
$node = $form_state['node'];
|
||||
$langcode = pathauto_entity_language('node', $node);
|
||||
pathauto_field_attach_form('node', $node, $form, $form_state, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_operations().
|
||||
*/
|
||||
function pathauto_node_operations() {
|
||||
$operations['pathauto_update_alias'] = array(
|
||||
'label' => t('Update URL alias'),
|
||||
'callback' => 'pathauto_node_update_alias_multiple',
|
||||
'callback arguments' => array('bulkupdate', array('message' => TRUE)),
|
||||
);
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL aliases for an individual node.
|
||||
*
|
||||
* @param $node
|
||||
* A node object.
|
||||
* @param $op
|
||||
* Operation being performed on the node ('insert', 'update' or 'bulkupdate').
|
||||
* @param $options
|
||||
* An optional array of additional options.
|
||||
*/
|
||||
function pathauto_node_update_alias(stdClass $node, $op, array $options = array()) {
|
||||
// Skip processing if the user has disabled pathauto for the node.
|
||||
if (isset($node->path['pathauto']) && empty($node->path['pathauto'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$options += array('language' => pathauto_entity_language('node', $node));
|
||||
|
||||
// Skip processing if the node has no pattern.
|
||||
if (!pathauto_pattern_load_by_entity('node', $node->type, $options['language'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
module_load_include('inc', 'pathauto');
|
||||
$uri = entity_uri('node', $node);
|
||||
pathauto_create_alias('node', $op, $uri['path'], array('node' => $node), $node->type, $options['language']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL aliases for multiple nodes.
|
||||
*
|
||||
* @param $nids
|
||||
* An array of node IDs.
|
||||
* @param $op
|
||||
* Operation being performed on the nodes ('insert', 'update' or
|
||||
* 'bulkupdate').
|
||||
* @param $options
|
||||
* An optional array of additional options.
|
||||
*/
|
||||
function pathauto_node_update_alias_multiple(array $nids, $op, array $options = array()) {
|
||||
$options += array('message' => FALSE);
|
||||
|
||||
$nodes = node_load_multiple($nids);
|
||||
foreach ($nodes as $node) {
|
||||
pathauto_node_update_alias($node, $op, $options);
|
||||
}
|
||||
|
||||
if (!empty($options['message'])) {
|
||||
drupal_set_message(format_plural(count($nids), 'Updated URL alias for 1 node.', 'Updated URL aliases for @count nodes.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update action wrapper for pathauto_node_update_alias().
|
||||
*/
|
||||
function pathauto_node_update_action($node, $context = array()) {
|
||||
pathauto_node_update_alias($node, 'bulkupdate', array('message' => TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "name pathauto_node".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name pathauto_taxonomy Pathauto integration for the core taxonomy module.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_term_insert().
|
||||
*/
|
||||
function pathauto_taxonomy_term_insert($term) {
|
||||
pathauto_taxonomy_term_update_alias($term, 'insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_term_update().
|
||||
*/
|
||||
function pathauto_taxonomy_term_update($term) {
|
||||
pathauto_taxonomy_term_update_alias($term, 'update', array('alias children' => TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_term_delete().
|
||||
*/
|
||||
function pathauto_taxonomy_term_delete($term) {
|
||||
pathauto_entity_path_delete_all('taxonomy_term', $term, "taxonomy/term/{$term->tid}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* Add the Pathauto settings to the taxonomy term form.
|
||||
*/
|
||||
function pathauto_form_taxonomy_form_term_alter(&$form, $form_state) {
|
||||
$term = $form_state['term'];
|
||||
$langcode = pathauto_entity_language('taxonomy_term', $term);
|
||||
pathauto_field_attach_form('taxonomy_term', $term, $form, $form_state, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL aliases for an individual taxonomy term.
|
||||
*
|
||||
* @param $term
|
||||
* A taxonomy term object.
|
||||
* @param $op
|
||||
* Operation being performed on the term ('insert', 'update' or 'bulkupdate').
|
||||
* @param $options
|
||||
* An optional array of additional options.
|
||||
*/
|
||||
function pathauto_taxonomy_term_update_alias(stdClass $term, $op, array $options = array()) {
|
||||
// Skip processing if the user has disabled pathauto for the term.
|
||||
if (isset($term->path['pathauto']) && empty($term->path['pathauto'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$module = 'taxonomy_term';
|
||||
if ($term->vid == variable_get('forum_nav_vocabulary', '')) {
|
||||
if (module_exists('forum')) {
|
||||
$module = 'forum';
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the term has its bundle, which is the vocabulary's machine name.
|
||||
if (!isset($term->vocabulary_machine_name)) {
|
||||
$vocabulary = taxonomy_vocabulary_load($term->vid);
|
||||
$term->vocabulary_machine_name = $vocabulary->machine_name;
|
||||
}
|
||||
|
||||
$options += array(
|
||||
'alias children' => FALSE,
|
||||
'language' => pathauto_entity_language('taxonomy_term', $term),
|
||||
);
|
||||
|
||||
// Skip processing if the term has no pattern.
|
||||
if (!pathauto_pattern_load_by_entity($module, $term->vocabulary_machine_name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
module_load_include('inc', 'pathauto');
|
||||
$uri = entity_uri('taxonomy_term', $term);
|
||||
pathauto_create_alias($module, $op, $uri['path'], array('term' => $term), $term->vocabulary_machine_name, $options['language']);
|
||||
|
||||
if (!empty($options['alias children'])) {
|
||||
// For all children generate new aliases.
|
||||
$options['alias children'] = FALSE;
|
||||
unset($options['language']);
|
||||
foreach (taxonomy_get_tree($term->vid, $term->tid) as $subterm) {
|
||||
pathauto_taxonomy_term_update_alias($subterm, $op, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL aliases for multiple taxonomy terms.
|
||||
*
|
||||
* @param $tids
|
||||
* An array of term IDs.
|
||||
* @param $op
|
||||
* Operation being performed on the nodes ('insert', 'update' or
|
||||
* 'bulkupdate').
|
||||
* @param $options
|
||||
* An optional array of additional options.
|
||||
*/
|
||||
function pathauto_taxonomy_term_update_alias_multiple(array $tids, $op, array $options = array()) {
|
||||
$options += array('message' => FALSE);
|
||||
|
||||
$terms = taxonomy_term_load_multiple($tids);
|
||||
foreach ($terms as $term) {
|
||||
pathauto_taxonomy_term_update_alias($term, $op, $options);
|
||||
}
|
||||
|
||||
if (!empty($options['message'])) {
|
||||
drupal_set_message(format_plural(count($tids), 'Updated URL alias for 1 term.', 'Updated URL aliases for @count terms.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update action wrapper for pathauto_taxonomy_term_update_alias().
|
||||
*/
|
||||
function pathauto_taxonomy_term_update_action($term, $context = array()) {
|
||||
pathauto_taxonomy_term_update_alias($term, 'bulkupdate', array('message' => TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "name pathauto_taxonomy".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name pathauto_user Pathauto integration for the core user and blog modules.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_user_insert().
|
||||
*/
|
||||
function pathauto_user_insert(&$edit, $account, $category) {
|
||||
pathauto_user_update_alias($account, 'insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_update().
|
||||
*/
|
||||
function pathauto_user_update(&$edit, $account, $category) {
|
||||
pathauto_user_update_alias($account, 'update');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_delete().
|
||||
*/
|
||||
function pathauto_user_delete($account) {
|
||||
pathauto_entity_path_delete_all('user', $account, "user/{$account->uid}");
|
||||
pathauto_path_delete_all("blog/{$account->uid}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_user_operations().
|
||||
*/
|
||||
function pathauto_user_operations() {
|
||||
$operations['pathauto_update_alias'] = array(
|
||||
'label' => t('Update URL alias'),
|
||||
'callback' => 'pathauto_user_update_alias_multiple',
|
||||
'callback arguments' => array('bulkupdate', array('message' => TRUE)),
|
||||
);
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL aliases for an individual user account.
|
||||
*
|
||||
* @param $account
|
||||
* A user account object.
|
||||
* @param $op
|
||||
* Operation being performed on the account ('insert', 'update' or
|
||||
* 'bulkupdate').
|
||||
* @param $options
|
||||
* An optional array of additional options.
|
||||
*/
|
||||
function pathauto_user_update_alias(stdClass $account, $op, array $options = array()) {
|
||||
// Skip processing if the user has disabled pathauto for the account.
|
||||
if (isset($account->path['pathauto']) && empty($account->path['pathauto'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$options += array(
|
||||
'alias blog' => module_exists('blog'),
|
||||
// $user->language is not the user entity language, thus we need to skip
|
||||
// the property fallback check.
|
||||
'language' => pathauto_entity_language('user', $account, FALSE),
|
||||
);
|
||||
|
||||
// Skip processing if the account has no pattern.
|
||||
if (!pathauto_pattern_load_by_entity('user', '', $options['language'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
module_load_include('inc', 'pathauto');
|
||||
$uri = entity_uri('user', $account);
|
||||
pathauto_create_alias('user', $op, $uri['path'], array('user' => $account), NULL, $options['language']);
|
||||
|
||||
// Because blogs are also associated with users, also generate the blog paths.
|
||||
if (!empty($options['alias blog'])) {
|
||||
pathauto_blog_update_alias($account, $op, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL aliases for multiple user accounts.
|
||||
*
|
||||
* @param $uids
|
||||
* An array of user account IDs.
|
||||
* @param $op
|
||||
* Operation being performed on the accounts ('insert', 'update' or
|
||||
* 'bulkupdate').
|
||||
* @param $options
|
||||
* An optional array of additional options.
|
||||
*/
|
||||
function pathauto_user_update_alias_multiple(array $uids, $op, array $options = array()) {
|
||||
$options += array('message' => FALSE);
|
||||
|
||||
$accounts = user_load_multiple($uids);
|
||||
foreach ($accounts as $account) {
|
||||
pathauto_user_update_alias($account, $op, $options);
|
||||
}
|
||||
|
||||
if (!empty($options['message'])) {
|
||||
drupal_set_message(format_plural(count($uids), 'Updated URL alias for 1 user account.', 'Updated URL aliases for @count user accounts.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update action wrapper for pathauto_user_update_alias().
|
||||
*/
|
||||
function pathauto_user_update_action($account, $context = array()) {
|
||||
pathauto_user_update_alias($account, 'bulkupdate', array('message' => TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the blog URL aliases for an individual user account.
|
||||
*
|
||||
* @param $account
|
||||
* A user account object.
|
||||
* @param $op
|
||||
* Operation being performed on the blog ('insert', 'update' or
|
||||
* 'bulkupdate').
|
||||
* @param $options
|
||||
* An optional array of additional options.
|
||||
*/
|
||||
function pathauto_blog_update_alias(stdClass $account, $op, array $options = array()) {
|
||||
// Skip processing if the blog has no pattern.
|
||||
if (!pathauto_pattern_load_by_entity('blog')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$options += array(
|
||||
'language' => LANGUAGE_NONE,
|
||||
);
|
||||
|
||||
module_load_include('inc', 'pathauto');
|
||||
if (node_access('create', 'blog', $account)) {
|
||||
pathauto_create_alias('blog', $op, "blog/{$account->uid}", array('user' => $account), NULL, $options['language']);
|
||||
}
|
||||
else {
|
||||
pathauto_path_delete_all("blog/{$account->uid}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "name pathauto_user".
|
||||
*/
|
392
sites/all/modules/contrib/content/pathauto/pathauto.pathauto.inc
Normal file
392
sites/all/modules/contrib/content/pathauto/pathauto.pathauto.inc
Normal file
@@ -0,0 +1,392 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Pathauto integration for core modules.
|
||||
*
|
||||
* @ingroup pathauto
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_path_alias_types().
|
||||
*
|
||||
* Used primarily by the bulk delete form.
|
||||
*/
|
||||
function pathauto_path_alias_types() {
|
||||
$objects['user/'] = t('Users');
|
||||
$objects['node/'] = t('Content');
|
||||
if (module_exists('blog')) {
|
||||
$objects['blog/'] = t('User blogs');
|
||||
}
|
||||
if (module_exists('taxonomy')) {
|
||||
$objects['taxonomy/term/'] = t('Taxonomy terms');
|
||||
}
|
||||
if (module_exists('forum')) {
|
||||
$objects['forum/'] = t('Forums');
|
||||
}
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_pathauto().
|
||||
*
|
||||
* This function is empty so that the other core module implementations can be
|
||||
* defined in this file. This is because in pathauto_module_implements_alter()
|
||||
* we add pathauto to be included first. The module system then peforms a
|
||||
* check on any subsequent run if this function still exists. If this does not
|
||||
* exist, than this file will not get included and the core implementations
|
||||
* will never get run.
|
||||
*
|
||||
* @see pathauto_module_implements_alter().
|
||||
*/
|
||||
function pathauto_pathauto() {
|
||||
// Empty hook; see the above comment.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_pathauto().
|
||||
*/
|
||||
function node_pathauto($op) {
|
||||
switch ($op) {
|
||||
case 'settings':
|
||||
$settings = array();
|
||||
$settings['module'] = 'node';
|
||||
$settings['token_type'] = 'node';
|
||||
$settings['groupheader'] = t('Content paths');
|
||||
$settings['patterndescr'] = t('Default path pattern (applies to all content types with blank patterns below)');
|
||||
$settings['patterndefault'] = 'content/[node:title]';
|
||||
$settings['batch_update_callback'] = 'node_pathauto_bulk_update_batch_process';
|
||||
$settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc';
|
||||
|
||||
$languages = array();
|
||||
if (module_exists('locale')) {
|
||||
$languages = array(LANGUAGE_NONE => t('language neutral')) + locale_language_list('name');
|
||||
}
|
||||
|
||||
foreach (node_type_get_names() as $node_type => $node_name) {
|
||||
if (count($languages) && variable_get('language_content_type_' . $node_type, 0)) {
|
||||
$settings['patternitems'][$node_type] = t('Default path pattern for @node_type (applies to all @node_type content types with blank patterns below)', array('@node_type' => $node_name));
|
||||
foreach ($languages as $lang_code => $lang_name) {
|
||||
$settings['patternitems'][$node_type . '_' . $lang_code] = t('Pattern for all @language @node_type paths', array('@node_type' => $node_name, '@language' => $lang_name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$settings['patternitems'][$node_type] = t('Pattern for all @node_type paths', array('@node_type' => $node_name));
|
||||
}
|
||||
}
|
||||
return (object) $settings;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch processing callback; Generate aliases for nodes.
|
||||
*/
|
||||
function node_pathauto_bulk_update_batch_process(&$context) {
|
||||
if (!isset($context['sandbox']['current'])) {
|
||||
$context['sandbox']['count'] = 0;
|
||||
$context['sandbox']['current'] = 0;
|
||||
}
|
||||
|
||||
$query = db_select('node', 'n');
|
||||
$query->leftJoin('url_alias', 'ua', "CONCAT('node/', n.nid) = ua.source");
|
||||
$query->addField('n', 'nid');
|
||||
$query->isNull('ua.source');
|
||||
$query->condition('n.nid', $context['sandbox']['current'], '>');
|
||||
$query->orderBy('n.nid');
|
||||
$query->addTag('pathauto_bulk_update');
|
||||
$query->addMetaData('entity', 'node');
|
||||
|
||||
// Get the total amount of items to process.
|
||||
if (!isset($context['sandbox']['total'])) {
|
||||
$context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
// If there are no nodes to update, the stop immediately.
|
||||
if (!$context['sandbox']['total']) {
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$query->range(0, 25);
|
||||
$nids = $query->execute()->fetchCol();
|
||||
|
||||
pathauto_node_update_alias_multiple($nids, 'bulkupdate');
|
||||
$context['sandbox']['count'] += count($nids);
|
||||
$context['sandbox']['current'] = max($nids);
|
||||
$context['message'] = t('Updated alias for node @nid.', array('@nid' => end($nids)));
|
||||
|
||||
if ($context['sandbox']['count'] != $context['sandbox']['total']) {
|
||||
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_pathauto().
|
||||
*/
|
||||
function taxonomy_pathauto($op) {
|
||||
switch ($op) {
|
||||
case 'settings':
|
||||
$settings = array();
|
||||
$settings['module'] = 'taxonomy_term';
|
||||
$settings['token_type'] = 'term';
|
||||
$settings['groupheader'] = t('Taxonomy term paths');
|
||||
$settings['patterndescr'] = t('Default path pattern (applies to all vocabularies with blank patterns below)');
|
||||
$settings['patterndefault'] = '[term:vocabulary]/[term:name]';
|
||||
$settings['batch_update_callback'] = 'taxonomy_pathauto_bulk_update_batch_process';
|
||||
$settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc';
|
||||
|
||||
$vocabularies = taxonomy_get_vocabularies();
|
||||
if (count($vocabularies)) {
|
||||
$settings['patternitems'] = array();
|
||||
foreach ($vocabularies as $vid => $vocabulary) {
|
||||
if ($vid == variable_get('forum_nav_vocabulary', '')) {
|
||||
// Skip the forum vocabulary.
|
||||
continue;
|
||||
}
|
||||
$settings['patternitems'][$vocabulary->machine_name] = t('Pattern for all %vocab-name paths', array('%vocab-name' => $vocabulary->name));
|
||||
}
|
||||
}
|
||||
return (object) $settings;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch processing callback; Generate aliases for taxonomy terms.
|
||||
*/
|
||||
function taxonomy_pathauto_bulk_update_batch_process(&$context) {
|
||||
if (!isset($context['sandbox']['current'])) {
|
||||
$context['sandbox']['count'] = 0;
|
||||
$context['sandbox']['current'] = 0;
|
||||
}
|
||||
|
||||
$query = db_select('taxonomy_term_data', 'td');
|
||||
$query->leftJoin('url_alias', 'ua', "CONCAT('taxonomy/term/', td.tid) = ua.source");
|
||||
$query->addField('td', 'tid');
|
||||
$query->isNull('ua.source');
|
||||
$query->condition('td.tid', $context['sandbox']['current'], '>');
|
||||
// Exclude the forums terms.
|
||||
if ($forum_vid = variable_get('forum_nav_vocabulary', '')) {
|
||||
$query->condition('td.vid', $forum_vid, '<>');
|
||||
}
|
||||
$query->orderBy('td.tid');
|
||||
$query->addTag('pathauto_bulk_update');
|
||||
$query->addMetaData('entity', 'taxonomy_term');
|
||||
|
||||
// Get the total amount of items to process.
|
||||
if (!isset($context['sandbox']['total'])) {
|
||||
$context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
// If there are no nodes to update, the stop immediately.
|
||||
if (!$context['sandbox']['total']) {
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$query->range(0, 25);
|
||||
$tids = $query->execute()->fetchCol();
|
||||
|
||||
pathauto_taxonomy_term_update_alias_multiple($tids, 'bulkupdate');
|
||||
$context['sandbox']['count'] += count($tids);
|
||||
$context['sandbox']['current'] = max($tids);
|
||||
$context['message'] = t('Updated alias for term @tid.', array('@tid' => end($tids)));
|
||||
|
||||
if ($context['sandbox']['count'] != $context['sandbox']['total']) {
|
||||
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_pathauto() for forum module.
|
||||
*/
|
||||
function forum_pathauto($op) {
|
||||
switch ($op) {
|
||||
case 'settings':
|
||||
$settings = array();
|
||||
$settings['module'] = 'forum';
|
||||
$settings['token_type'] = 'term';
|
||||
$settings['groupheader'] = t('Forum paths');
|
||||
$settings['patterndescr'] = t('Pattern for forums and forum containers');
|
||||
$settings['patterndefault'] = '[term:vocabulary]/[term:name]';
|
||||
$settings['batch_update_callback'] = 'forum_pathauto_bulk_update_batch_process';
|
||||
$settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc';
|
||||
return (object) $settings;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch processing callback; Generate aliases for forums.
|
||||
*/
|
||||
function forum_pathauto_bulk_update_batch_process(&$context) {
|
||||
if (!isset($context['sandbox']['current'])) {
|
||||
$context['sandbox']['count'] = 0;
|
||||
$context['sandbox']['current'] = 0;
|
||||
}
|
||||
|
||||
$query = db_select('taxonomy_term_data', 'td');
|
||||
$query->leftJoin('url_alias', 'ua', "CONCAT('forum/', td.tid) = ua.source");
|
||||
$query->addField('td', 'tid');
|
||||
$query->isNull('ua.source');
|
||||
$query->condition('td.tid', $context['sandbox']['current'], '>');
|
||||
$query->condition('td.vid', variable_get('forum_nav_vocabulary', ''));
|
||||
$query->orderBy('td.tid');
|
||||
$query->addTag('pathauto_bulk_update');
|
||||
$query->addMetaData('entity', 'taxonomy_term');
|
||||
|
||||
// Get the total amount of items to process.
|
||||
if (!isset($context['sandbox']['total'])) {
|
||||
$context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
// If there are no nodes to update, the stop immediately.
|
||||
if (!$context['sandbox']['total']) {
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$query->range(0, 25);
|
||||
$tids = $query->execute()->fetchCol();
|
||||
|
||||
pathauto_taxonomy_term_update_alias_multiple($tids, 'bulkupdate');
|
||||
$context['sandbox']['count'] += count($tids);
|
||||
$context['sandbox']['current'] = max($tids);
|
||||
$context['message'] = t('Updated alias for forum @tid.', array('@tid' => end($tids)));
|
||||
|
||||
if ($context['sandbox']['count'] != $context['sandbox']['total']) {
|
||||
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_pathauto().
|
||||
*/
|
||||
function user_pathauto($op) {
|
||||
switch ($op) {
|
||||
case 'settings':
|
||||
$settings = array();
|
||||
$settings['module'] = 'user';
|
||||
$settings['token_type'] = 'user';
|
||||
$settings['groupheader'] = t('User paths');
|
||||
$settings['patterndescr'] = t('Pattern for user account page paths');
|
||||
$settings['patterndefault'] = 'users/[user:name]';
|
||||
$settings['batch_update_callback'] = 'user_pathauto_bulk_update_batch_process';
|
||||
$settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc';
|
||||
return (object) $settings;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch processing callback; Generate aliases for users.
|
||||
*/
|
||||
function user_pathauto_bulk_update_batch_process(&$context) {
|
||||
if (!isset($context['sandbox']['current'])) {
|
||||
$context['sandbox']['count'] = 0;
|
||||
$context['sandbox']['current'] = 0;
|
||||
}
|
||||
|
||||
$query = db_select('users', 'u');
|
||||
$query->leftJoin('url_alias', 'ua', "CONCAT('user/', u.uid) = ua.source");
|
||||
$query->addField('u', 'uid');
|
||||
$query->isNull('ua.source');
|
||||
$query->condition('u.uid', $context['sandbox']['current'], '>');
|
||||
$query->orderBy('u.uid');
|
||||
$query->addTag('pathauto_bulk_update');
|
||||
$query->addMetaData('entity', 'user');
|
||||
|
||||
// Get the total amount of items to process.
|
||||
if (!isset($context['sandbox']['total'])) {
|
||||
$context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
// If there are no nodes to update, the stop immediately.
|
||||
if (!$context['sandbox']['total']) {
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$query->range(0, 25);
|
||||
$uids = $query->execute()->fetchCol();
|
||||
|
||||
pathauto_user_update_alias_multiple($uids, 'bulkupdate', array('alias blog' => FALSE));
|
||||
$context['sandbox']['count'] += count($uids);
|
||||
$context['sandbox']['current'] = max($uids);
|
||||
$context['message'] = t('Updated alias for user @uid.', array('@uid' => end($uids)));
|
||||
|
||||
if ($context['sandbox']['count'] != $context['sandbox']['total']) {
|
||||
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_pathauto().
|
||||
*/
|
||||
function blog_pathauto($op) {
|
||||
switch ($op) {
|
||||
case 'settings':
|
||||
$settings = array();
|
||||
$settings['module'] = 'blog';
|
||||
$settings['token_type'] = 'user';
|
||||
$settings['groupheader'] = t('Blog paths');
|
||||
$settings['patterndescr'] = t('Pattern for blog page paths');
|
||||
$settings['patterndefault'] = 'blogs/[user:name]';
|
||||
$settings['batch_update_callback'] = 'blog_pathauto_bulk_update_batch_process';
|
||||
$settings['batch_file'] = drupal_get_path('module', 'pathauto') . '/pathauto.pathauto.inc';
|
||||
return (object) $settings;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch processing callback; Generate aliases for blogs.
|
||||
*/
|
||||
function blog_pathauto_bulk_update_batch_process(&$context) {
|
||||
if (!isset($context['sandbox']['current'])) {
|
||||
$context['sandbox']['count'] = 0;
|
||||
$context['sandbox']['current'] = 0;
|
||||
}
|
||||
|
||||
$query = db_select('users', 'u');
|
||||
$query->leftJoin('url_alias', 'ua', "CONCAT('blog/', u.uid) = ua.source");
|
||||
$query->addField('u', 'uid');
|
||||
$query->isNull('ua.source');
|
||||
$query->condition('u.uid', $context['sandbox']['current'], '>');
|
||||
$query->orderBy('u.uid');
|
||||
$query->addTag('pathauto_bulk_update');
|
||||
$query->addMetaData('entity', 'user');
|
||||
|
||||
// Get the total amount of items to process.
|
||||
if (!isset($context['sandbox']['total'])) {
|
||||
$context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
// If there are no nodes to update, the stop immediately.
|
||||
if (!$context['sandbox']['total']) {
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$query->range(0, 25);
|
||||
$uids = $query->execute()->fetchCol();
|
||||
|
||||
$accounts = user_load_multiple($uids);
|
||||
foreach ($accounts as $account) {
|
||||
pathauto_blog_update_alias($account, 'bulkupdate');
|
||||
}
|
||||
|
||||
$context['sandbox']['count'] += count($uids);
|
||||
$context['sandbox']['current'] = max($uids);
|
||||
$context['message'] = t('Updated alias for blog user @uid.', array('@uid' => end($uids)));
|
||||
|
||||
if ($context['sandbox']['count'] != $context['sandbox']['total']) {
|
||||
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
|
||||
}
|
||||
}
|
793
sites/all/modules/contrib/content/pathauto/pathauto.test
Normal file
793
sites/all/modules/contrib/content/pathauto/pathauto.test
Normal file
@@ -0,0 +1,793 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functionality tests for Pathauto.
|
||||
*
|
||||
* @ingroup pathauto
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper test class with some added functions for testing.
|
||||
*/
|
||||
class PathautoTestHelper extends DrupalWebTestCase {
|
||||
function setUp(array $modules = array()) {
|
||||
$modules[] = 'path';
|
||||
$modules[] = 'token';
|
||||
$modules[] = 'pathauto';
|
||||
$modules[] = 'taxonomy';
|
||||
parent::setUp($modules);
|
||||
}
|
||||
|
||||
function assertToken($type, $object, $token, $expected) {
|
||||
$tokens = token_generate($type, array($token => $token), array($type => $object));
|
||||
$tokens += array($token => '');
|
||||
$this->assertIdentical($tokens[$token], $expected, t("Token value for [@type:@token] was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $tokens[$token], '@expected' => $expected)));
|
||||
}
|
||||
|
||||
function saveAlias($source, $alias, $language = LANGUAGE_NONE) {
|
||||
$alias = array(
|
||||
'source' => $source,
|
||||
'alias' => $alias,
|
||||
'language' => $language,
|
||||
);
|
||||
path_save($alias);
|
||||
return $alias;
|
||||
}
|
||||
|
||||
function saveEntityAlias($entity_type, $entity, $alias, $language = LANGUAGE_NONE) {
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
return $this->saveAlias($uri['path'], $alias, $language);
|
||||
}
|
||||
|
||||
function assertEntityAlias($entity_type, $entity, $expected_alias, $language = LANGUAGE_NONE) {
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
$this->assertAlias($uri['path'], $expected_alias, $language);
|
||||
}
|
||||
|
||||
function assertEntityAliasExists($entity_type, $entity) {
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
return $this->assertAliasExists(array('source' => $uri['path']));
|
||||
}
|
||||
|
||||
function assertNoEntityAlias($entity_type, $entity, $language = LANGUAGE_NONE) {
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
$this->assertEntityAlias($entity_type, $entity, $uri['path'], $language);
|
||||
}
|
||||
|
||||
function assertNoEntityAliasExists($entity_type, $entity) {
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
$this->assertNoAliasExists(array('source' => $uri['path']));
|
||||
}
|
||||
|
||||
function assertAlias($source, $expected_alias, $language = LANGUAGE_NONE) {
|
||||
drupal_clear_path_cache($source);
|
||||
$alias = drupal_get_path_alias($source, $language);
|
||||
$this->assertIdentical($alias, $expected_alias, t("Alias for %source with language '@language' was %actual, expected %expected.", array('%source' => $source, '%actual' => $alias, '%expected' => $expected_alias, '@language' => $language)));
|
||||
}
|
||||
|
||||
function assertAliasExists($conditions) {
|
||||
$path = path_load($conditions);
|
||||
$this->assertTrue($path, t('Alias with conditions @conditions found.', array('@conditions' => var_export($conditions, TRUE))));
|
||||
return $path;
|
||||
}
|
||||
|
||||
function assertNoAliasExists($conditions) {
|
||||
$alias = path_load($conditions);
|
||||
$this->assertFalse($alias, t('Alias with conditions @conditions not found.', array('@conditions' => var_export($conditions, TRUE))));
|
||||
}
|
||||
|
||||
function deleteAllAliases() {
|
||||
db_delete('url_alias')->execute();
|
||||
drupal_clear_path_cache();
|
||||
}
|
||||
|
||||
function addVocabulary(array $vocabulary = array()) {
|
||||
$name = drupal_strtolower($this->randomName(5));
|
||||
$vocabulary += array(
|
||||
'name' => $name,
|
||||
'machine_name' => $name,
|
||||
'nodes' => array('article' => 'article'),
|
||||
);
|
||||
$vocabulary = (object) $vocabulary;
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
return $vocabulary;
|
||||
}
|
||||
|
||||
function addTerm(stdClass $vocabulary, array $term = array()) {
|
||||
$term += array(
|
||||
'name' => drupal_strtolower($this->randomName(5)),
|
||||
'vocabulary_machine_name' => $vocabulary->machine_name,
|
||||
'vid' => $vocabulary->vid,
|
||||
);
|
||||
$term = (object) $term;
|
||||
taxonomy_term_save($term);
|
||||
return $term;
|
||||
}
|
||||
|
||||
function assertEntityPattern($entity_type, $bundle, $language = LANGUAGE_NONE, $expected) {
|
||||
drupal_static_reset('pathauto_pattern_load_by_entity');
|
||||
$this->refreshVariables();
|
||||
$pattern = pathauto_pattern_load_by_entity($entity_type, $bundle, $language);
|
||||
$this->assertIdentical($expected, $pattern);
|
||||
}
|
||||
|
||||
function drupalGetTermByName($name, $reset = FALSE) {
|
||||
$terms = entity_load('taxonomy_term', array(), array('name' => $name), $reset);
|
||||
return !empty($terms) ? reset($terms) : FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit tests for Pathauto functions.
|
||||
*/
|
||||
class PathautoUnitTestCase extends PathautoTestHelper {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Pathauto unit tests',
|
||||
'description' => 'Unit tests for Pathauto functions.',
|
||||
'group' => 'Pathauto',
|
||||
'dependencies' => array('token'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp(array $modules = array()) {
|
||||
parent::setUp($modules);
|
||||
module_load_include('inc', 'pathauto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test _pathauto_get_schema_alias_maxlength().
|
||||
*/
|
||||
function testGetSchemaAliasMaxLength() {
|
||||
$this->assertIdentical(_pathauto_get_schema_alias_maxlength(), 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pathauto_pattern_load_by_entity().
|
||||
*/
|
||||
function testPatternLoadByEntity() {
|
||||
variable_set('pathauto_node_story_en_pattern', ' story/en/[node:title] ');
|
||||
variable_set('pathauto_node_story_pattern', 'story/[node:title]');
|
||||
variable_set('pathauto_node_pattern', 'content/[node:title]');
|
||||
variable_set('pathauto_user_pattern', 'users/[user:name]');
|
||||
|
||||
$tests = array(
|
||||
array('entity' => 'node', 'bundle' => 'story', 'language' => 'fr', 'expected' => 'story/[node:title]'),
|
||||
array('entity' => 'node', 'bundle' => 'story', 'language' => 'en', 'expected' => 'story/en/[node:title]'),
|
||||
array('entity' => 'node', 'bundle' => 'story', 'language' => LANGUAGE_NONE, 'expected' => 'story/[node:title]'),
|
||||
array('entity' => 'node', 'bundle' => 'page', 'language' => 'en', 'expected' => 'content/[node:title]'),
|
||||
array('entity' => 'user', 'bundle' => 'user', 'language' => LANGUAGE_NONE, 'expected' => 'users/[user:name]'),
|
||||
array('entity' => 'invalid-entity', 'bundle' => '', 'language' => LANGUAGE_NONE, 'expected' => ''),
|
||||
);
|
||||
foreach ($tests as $test) {
|
||||
$actual = pathauto_pattern_load_by_entity($test['entity'], $test['bundle'], $test['language']);
|
||||
$this->assertIdentical($actual, $test['expected'], t("pathauto_pattern_load_by_entity('@entity', '@bundle', '@language') returned '@actual', expected '@expected'", array('@entity' => $test['entity'], '@bundle' => $test['bundle'], '@language' => $test['language'], '@actual' => $actual, '@expected' => $test['expected'])));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pathauto_cleanstring().
|
||||
*/
|
||||
function testCleanString() {
|
||||
$tests = array();
|
||||
variable_set('pathauto_ignore_words', ', in, is,that, the , this, with, ');
|
||||
variable_set('pathauto_max_component_length', 35);
|
||||
|
||||
// Test the 'ignored words' removal.
|
||||
$tests['this'] = 'this';
|
||||
$tests['this with that'] = 'this-with-that';
|
||||
$tests['this thing with that thing'] = 'thing-thing';
|
||||
|
||||
// Test length truncation and duplicate separator removal.
|
||||
$tests[' - Pathauto is the greatest - module ever in Drupal history - '] = 'pathauto-greatest-module-ever';
|
||||
|
||||
// Test that HTML tags are removed.
|
||||
$tests['This <span class="text">text</span> has <br /><a href="http://example.com"><strong>HTML tags</strong></a>.'] = 'text-has-html-tags';
|
||||
$tests[check_plain('This <span class="text">text</span> has <br /><a href="http://example.com"><strong>HTML tags</strong></a>.')] = 'text-has-html-tags';
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = pathauto_cleanstring($input);
|
||||
$this->assertEqual($output, $expected, t("pathauto_cleanstring('@input') expected '@expected', actual '@output'", array('@input' => $input, '@expected' => $expected, '@output' => $output)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pathauto_path_delete_multiple().
|
||||
*/
|
||||
function testPathDeleteMultiple() {
|
||||
$this->saveAlias('node/1', 'node-1-alias');
|
||||
$this->saveAlias('node/1/view', 'node-1-alias/view');
|
||||
$this->saveAlias('node/1', 'node-1-alias-en', 'en');
|
||||
$this->saveAlias('node/1', 'node-1-alias-fr', 'fr');
|
||||
$this->saveAlias('node/2', 'node-2-alias');
|
||||
|
||||
pathauto_path_delete_all('node/1');
|
||||
$this->assertNoAliasExists(array('source' => "node/1"));
|
||||
$this->assertNoAliasExists(array('source' => "node/1/view"));
|
||||
$this->assertAliasExists(array('source' => "node/2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the different update actions in pathauto_create_alias().
|
||||
*/
|
||||
function testUpdateActions() {
|
||||
// Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'insert'.
|
||||
variable_set('pathauto_update_action', PATHAUTO_UPDATE_ACTION_NO_NEW);
|
||||
$node = $this->drupalCreateNode(array('title' => 'First title'));
|
||||
$this->assertEntityAlias('node', $node, 'content/first-title');
|
||||
|
||||
// Default action is PATHAUTO_UPDATE_ACTION_DELETE.
|
||||
variable_set('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE);
|
||||
$node->title = 'Second title';
|
||||
pathauto_node_update($node);
|
||||
$this->assertEntityAlias('node', $node, 'content/second-title');
|
||||
$this->assertNoAliasExists(array('alias' => 'content/first-title'));
|
||||
|
||||
// Test PATHAUTO_UPDATE_ACTION_LEAVE
|
||||
variable_set('pathauto_update_action', PATHAUTO_UPDATE_ACTION_LEAVE);
|
||||
$node->title = 'Third title';
|
||||
pathauto_node_update($node);
|
||||
$this->assertEntityAlias('node', $node, 'content/third-title');
|
||||
$this->assertAliasExists(array('source' => "node/{$node->nid}", 'alias' => 'content/second-title'));
|
||||
|
||||
variable_set('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE);
|
||||
$node->title = 'Fourth title';
|
||||
pathauto_node_update($node);
|
||||
$this->assertEntityAlias('node', $node, 'content/fourth-title');
|
||||
$this->assertNoAliasExists(array('alias' => 'content/third-title'));
|
||||
// The older second alias is not deleted yet.
|
||||
$older_path = $this->assertAliasExists(array('source' => "node/{$node->nid}", 'alias' => 'content/second-title'));
|
||||
path_delete($older_path);
|
||||
|
||||
variable_set('pathauto_update_action', PATHAUTO_UPDATE_ACTION_NO_NEW);
|
||||
$node->title = 'Fifth title';
|
||||
pathauto_node_update($node);
|
||||
$this->assertEntityAlias('node', $node, 'content/fourth-title');
|
||||
$this->assertNoAliasExists(array('alias' => 'content/fith-title'));
|
||||
|
||||
// Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'update'.
|
||||
$this->deleteAllAliases();
|
||||
pathauto_node_update($node);
|
||||
$this->assertEntityAlias('node', $node, 'content/fifth-title');
|
||||
|
||||
// Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'bulkupdate'.
|
||||
$this->deleteAllAliases();
|
||||
$node->title = 'Sixth title';
|
||||
pathauto_node_update_alias($node, 'bulkupdate');
|
||||
$this->assertEntityAlias('node', $node, 'content/sixth-title');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that pathauto_create_alias() will not create an alias for a pattern
|
||||
* that does not get any tokens replaced.
|
||||
*/
|
||||
function testNoTokensNoAlias() {
|
||||
$node = $this->drupalCreateNode(array('title' => ''));
|
||||
$this->assertNoEntityAliasExists('node', $node);
|
||||
|
||||
$node->title = 'hello';
|
||||
pathauto_node_update($node);
|
||||
$this->assertEntityAlias('node', $node, 'content/hello');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the handling of path vs non-path tokens in pathauto_clean_token_values().
|
||||
*/
|
||||
function testPathTokens() {
|
||||
variable_set('pathauto_taxonomy_term_pattern', '[term:parent:url:path]/[term:name]');
|
||||
$vocab = $this->addVocabulary();
|
||||
|
||||
$term1 = $this->addTerm($vocab, array('name' => 'Parent term'));
|
||||
$this->assertEntityAlias('taxonomy_term', $term1, 'parent-term');
|
||||
|
||||
$term2 = $this->addTerm($vocab, array('name' => 'Child term', 'parent' => $term1->tid));
|
||||
$this->assertEntityAlias('taxonomy_term', $term2, 'parent-term/child-term');
|
||||
|
||||
$this->saveEntityAlias('taxonomy_term', $term1, 'My Crazy/Alias/');
|
||||
pathauto_taxonomy_term_update($term2);
|
||||
$this->assertEntityAlias('taxonomy_term', $term2, 'My Crazy/Alias/child-term');
|
||||
}
|
||||
|
||||
function testEntityBundleRenamingDeleting() {
|
||||
// Create a vocabulary and test that it's pattern variable works.
|
||||
$vocab = $this->addVocabulary(array('machine_name' => 'old_name'));
|
||||
variable_set('pathauto_taxonomy_term_pattern', 'base');
|
||||
variable_set("pathauto_taxonomy_term_old_name_pattern", 'bundle');
|
||||
$this->assertEntityPattern('taxonomy_term', 'old_name', LANGUAGE_NONE, 'bundle');
|
||||
|
||||
// Rename the vocabulary's machine name, which should cause its pattern
|
||||
// variable to also be renamed.
|
||||
$vocab->machine_name = 'new_name';
|
||||
taxonomy_vocabulary_save($vocab);
|
||||
$this->assertEntityPattern('taxonomy_term', 'new_name', LANGUAGE_NONE, 'bundle');
|
||||
$this->assertEntityPattern('taxonomy_term', 'old_name', LANGUAGE_NONE, 'base');
|
||||
|
||||
// Delete the vocabulary, which should cause its pattern variable to also
|
||||
// be deleted.
|
||||
taxonomy_vocabulary_delete($vocab->vid);
|
||||
$this->assertEntityPattern('taxonomy_term', 'new_name', LANGUAGE_NONE, 'base');
|
||||
}
|
||||
|
||||
function testNoExistingPathAliases() {
|
||||
variable_set('pathauto_node_page_pattern', '[node:title]');
|
||||
variable_set('pathauto_punctuation_period', PATHAUTO_PUNCTUATION_DO_NOTHING);
|
||||
|
||||
// Check that Pathauto does not create an alias of '/admin'.
|
||||
$node = $this->drupalCreateNode(array('title' => 'Admin', 'type' => 'page'));
|
||||
$this->assertNoEntityAlias('node', $node);
|
||||
|
||||
// Check that Pathauto does not create an alias of '/modules'.
|
||||
$node->title = 'Modules';
|
||||
node_save($node);
|
||||
$this->assertNoEntityAlias('node', $node);
|
||||
|
||||
// Check that Pathauto does not create an alias of '/index.php'.
|
||||
$node->title = 'index.php';
|
||||
node_save($node);
|
||||
$this->assertNoEntityAlias('node', $node);
|
||||
|
||||
// Check that a safe value gets an automatic alias. This is also a control
|
||||
// to ensure the above tests work properly.
|
||||
$node->title = 'Safe value';
|
||||
node_save($node);
|
||||
$this->assertEntityAlias('node', $node, 'safe-value');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper test class with some added functions for testing.
|
||||
*/
|
||||
class PathautoFunctionalTestHelper extends PathautoTestHelper {
|
||||
protected $admin_user;
|
||||
|
||||
function setUp(array $modules = array()) {
|
||||
parent::setUp($modules);
|
||||
|
||||
// Set pathauto settings we assume to be as-is in this test.
|
||||
variable_set('pathauto_node_page_pattern', 'content/[node:title]');
|
||||
|
||||
// Allow other modules to add additional permissions for the admin user.
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer nodes',
|
||||
'bypass node access',
|
||||
'access content overview',
|
||||
'administer taxonomy',
|
||||
'administer users',
|
||||
);
|
||||
$args = func_get_args();
|
||||
if (isset($args[1]) && is_array($args[1])) {
|
||||
$permissions = array_merge($permissions, $args[1]);
|
||||
}
|
||||
$this->admin_user = $this->drupalCreateUser($permissions);
|
||||
|
||||
$this->drupalLogin($this->admin_user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic pathauto functionality.
|
||||
*/
|
||||
class PathautoFunctionalTestCase extends PathautoFunctionalTestHelper {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Pathauto basic tests',
|
||||
'description' => 'Test basic pathauto functionality.',
|
||||
'group' => 'Pathauto',
|
||||
'dependencies' => array('token'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic functional testing of Pathauto.
|
||||
*/
|
||||
function testNodeEditing() {
|
||||
// Delete the default node pattern. Only the page content type will have a pattern.
|
||||
variable_del('pathauto_node_pattern');
|
||||
|
||||
// Ensure that the Pathauto checkbox is checked by default on the node add form.
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertFieldChecked('edit-path-pathauto');
|
||||
|
||||
// Create node for testing by previewing and saving the node form.
|
||||
$title = ' Testing: node title [';
|
||||
$automatic_alias = 'content/testing-node-title';
|
||||
$this->drupalPost(NULL, array('title' => $title), 'Preview');
|
||||
$this->drupalPost(NULL, array(), 'Save');
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
|
||||
// Look for alias generated in the form.
|
||||
$this->drupalGet("node/{$node->nid}/edit");
|
||||
$this->assertFieldChecked('edit-path-pathauto');
|
||||
$this->assertFieldByName('path[alias]', $automatic_alias, 'Generated alias visible in the path alias field.');
|
||||
|
||||
// Check whether the alias actually works.
|
||||
$this->drupalGet($automatic_alias);
|
||||
$this->assertText($title, 'Node accessible through automatic alias.');
|
||||
|
||||
// Manually set the node's alias.
|
||||
$manual_alias = 'content/' . $node->nid;
|
||||
$edit = array(
|
||||
'path[pathauto]' => FALSE,
|
||||
'path[alias]' => $manual_alias,
|
||||
);
|
||||
$this->drupalPost("node/{$node->nid}/edit", $edit, t('Save'));
|
||||
$this->assertText("Basic page $title has been updated.");
|
||||
|
||||
// Check that the automatic alias checkbox is now unchecked by default.
|
||||
$this->drupalGet("node/{$node->nid}/edit");
|
||||
$this->assertNoFieldChecked('edit-path-pathauto');
|
||||
$this->assertFieldByName('path[alias]', $manual_alias);
|
||||
|
||||
// Submit the node form with the default values.
|
||||
$this->drupalPost(NULL, array(), t('Save'));
|
||||
$this->assertText("Basic page $title has been updated.");
|
||||
|
||||
// Test that the old (automatic) alias has been deleted and only accessible
|
||||
// through the new (manual) alias.
|
||||
$this->drupalGet($automatic_alias);
|
||||
$this->assertResponse(404, 'Node not accessible through automatic alias.');
|
||||
$this->drupalGet($manual_alias);
|
||||
$this->assertText($title, 'Node accessible through manual alias.');
|
||||
|
||||
// Now attempt to create a node that has no pattern (article content type).
|
||||
// The Pathauto checkbox should not exist.
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertNoFieldById('edit-path-pathauto');
|
||||
$this->assertFieldByName('path[alias]', '');
|
||||
|
||||
$edit = array();
|
||||
$edit['title'] = 'My test article';
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
|
||||
// Pathauto checkbox should still not exist.
|
||||
$this->drupalGet('node/' . $node->nid . '/edit');
|
||||
$this->assertNoFieldById('edit-path-pathauto');
|
||||
$this->assertFieldByName('path[alias]', '');
|
||||
$this->assertNoEntityAlias('node', $node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test node operations.
|
||||
*/
|
||||
function testNodeOperations() {
|
||||
$node1 = $this->drupalCreateNode(array('title' => 'node1'));
|
||||
$node2 = $this->drupalCreateNode(array('title' => 'node2'));
|
||||
|
||||
// Delete all current URL aliases.
|
||||
$this->deleteAllAliases();
|
||||
|
||||
$edit = array(
|
||||
'operation' => 'pathauto_update_alias',
|
||||
"nodes[{$node1->nid}]" => TRUE,
|
||||
);
|
||||
$this->drupalPost('admin/content', $edit, t('Update'));
|
||||
$this->assertText('Updated URL alias for 1 node.');
|
||||
|
||||
$this->assertEntityAlias('node', $node1, 'content/' . $node1->title);
|
||||
$this->assertEntityAlias('node', $node2, 'node/' . $node2->nid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic functional testing of Pathauto with taxonomy terms.
|
||||
*/
|
||||
function testTermEditing() {
|
||||
$this->drupalGet('admin/structure');
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
|
||||
// Create term for testing.
|
||||
$name = ' Testing: term name [ ';
|
||||
$automatic_alias = 'tags/testing-term-name';
|
||||
$this->drupalPost('admin/structure/taxonomy/tags/add', array('name' => $name), 'Save');
|
||||
$name = trim($name);
|
||||
$this->assertText("Created new term $name.");
|
||||
$term = $this->drupalGetTermByName($name);
|
||||
|
||||
// Look for alias generated in the form.
|
||||
$this->drupalGet("taxonomy/term/{$term->tid}/edit");
|
||||
$this->assertFieldChecked('edit-path-pathauto');
|
||||
$this->assertFieldByName('path[alias]', $automatic_alias, 'Generated alias visible in the path alias field.');
|
||||
|
||||
// Check whether the alias actually works.
|
||||
$this->drupalGet($automatic_alias);
|
||||
$this->assertText($name, 'Term accessible through automatic alias.');
|
||||
|
||||
// Manually set the term's alias.
|
||||
$manual_alias = 'tags/' . $term->tid;
|
||||
$edit = array(
|
||||
'path[pathauto]' => FALSE,
|
||||
'path[alias]' => $manual_alias,
|
||||
);
|
||||
$this->drupalPost("taxonomy/term/{$term->tid}/edit", $edit, t('Save'));
|
||||
$this->assertText("Updated term $name.");
|
||||
|
||||
// Check that the automatic alias checkbox is now unchecked by default.
|
||||
$this->drupalGet("taxonomy/term/{$term->tid}/edit");
|
||||
$this->assertNoFieldChecked('edit-path-pathauto');
|
||||
$this->assertFieldByName('path[alias]', $manual_alias);
|
||||
|
||||
// Submit the term form with the default values.
|
||||
$this->drupalPost(NULL, array(), t('Save'));
|
||||
$this->assertText("Updated term $name.");
|
||||
|
||||
// Test that the old (automatic) alias has been deleted and only accessible
|
||||
// through the new (manual) alias.
|
||||
$this->drupalGet($automatic_alias);
|
||||
$this->assertResponse(404, 'Term not accessible through automatic alias.');
|
||||
$this->drupalGet($manual_alias);
|
||||
$this->assertText($name, 'Term accessible through manual alias.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic functional testing of Pathauto with users.
|
||||
*/
|
||||
function testUserEditing() {
|
||||
// There should be no Pathauto checkbox on user forms.
|
||||
$this->drupalGet('user/' . $this->admin_user->uid . '/edit');
|
||||
$this->assertNoFieldById('edit-path-pathauto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test user operations.
|
||||
*/
|
||||
function testUserOperations() {
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Delete all current URL aliases.
|
||||
$this->deleteAllAliases();
|
||||
|
||||
$edit = array(
|
||||
'operation' => 'pathauto_update_alias',
|
||||
"accounts[{$account->uid}]" => TRUE,
|
||||
);
|
||||
$this->drupalPost('admin/people', $edit, t('Update'));
|
||||
$this->assertText('Updated URL alias for 1 user account.');
|
||||
|
||||
$this->assertEntityAlias('user', $account, 'users/' . drupal_strtolower($account->name));
|
||||
$this->assertEntityAlias('user', $this->admin_user, 'user/' . $this->admin_user->uid);
|
||||
}
|
||||
|
||||
function testSettingsValidation() {
|
||||
$edit = array();
|
||||
$edit['pathauto_max_length'] = 'abc';
|
||||
$edit['pathauto_max_component_length'] = 'abc';
|
||||
$this->drupalPost('admin/config/search/path/settings', $edit, 'Save configuration');
|
||||
$this->assertText('The field Maximum alias length is not a valid number.');
|
||||
$this->assertText('The field Maximum component length is not a valid number.');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$edit['pathauto_max_length'] = '0';
|
||||
$edit['pathauto_max_component_length'] = '0';
|
||||
$this->drupalPost('admin/config/search/path/settings', $edit, 'Save configuration');
|
||||
$this->assertText('The field Maximum alias length cannot be less than 1.');
|
||||
$this->assertText('The field Maximum component length cannot be less than 1.');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$edit['pathauto_max_length'] = '999';
|
||||
$edit['pathauto_max_component_length'] = '999';
|
||||
$this->drupalPost('admin/config/search/path/settings', $edit, 'Save configuration');
|
||||
$this->assertText('The field Maximum alias length cannot be greater than 255.');
|
||||
$this->assertText('The field Maximum component length cannot be greater than 255.');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$edit['pathauto_max_length'] = '50';
|
||||
$edit['pathauto_max_component_length'] = '50';
|
||||
$this->drupalPost('admin/config/search/path/settings', $edit, 'Save configuration');
|
||||
$this->assertText('The configuration options have been saved.');
|
||||
}
|
||||
|
||||
function testPatternsValidation() {
|
||||
$edit = array();
|
||||
$edit['pathauto_node_pattern'] = '[node:title]/[user:name]/[term:name]';
|
||||
$edit['pathauto_node_page_pattern'] = 'page';
|
||||
$this->drupalPost('admin/config/search/path/patterns', $edit, 'Save configuration');
|
||||
$this->assertText('The Default path pattern (applies to all content types with blank patterns below) is using the following invalid tokens: [user:name], [term:name].');
|
||||
$this->assertText('The Pattern for all Basic page paths cannot contain fewer than one token.');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$edit['pathauto_node_pattern'] = '[node:title]';
|
||||
$edit['pathauto_node_page_pattern'] = 'page/[node:title]';
|
||||
$edit['pathauto_node_article_pattern'] = '';
|
||||
$this->drupalPost('admin/config/search/path/patterns', $edit, 'Save configuration');
|
||||
$this->assertText('The configuration options have been saved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test programmatic entity creation for aliases.
|
||||
*/
|
||||
function testProgrammaticEntityCreation() {
|
||||
$node = $this->drupalCreateNode(array('title' => 'Test node', 'path' => array('pathauto' => TRUE)));
|
||||
$this->assertEntityAlias('node', $node, 'content/test-node');
|
||||
|
||||
$vocabulary = $this->addVocabulary(array('name' => 'Tags'));
|
||||
$term = $this->addTerm($vocabulary, array('name' => 'Test term', 'path' => array('pathauto' => TRUE)));
|
||||
$this->assertEntityAlias('taxonomy_term', $term, 'tags/test-term');
|
||||
|
||||
$edit['name'] = 'Test user';
|
||||
$edit['mail'] = 'test-user@example.com';
|
||||
$edit['pass'] = user_password();
|
||||
$edit['path'] = array('pathauto' => TRUE);
|
||||
$edit['status'] = 1;
|
||||
$account = user_save(drupal_anonymous_user(), $edit);
|
||||
$this->assertEntityAlias('user', $account, 'users/test-user');
|
||||
}
|
||||
}
|
||||
|
||||
class PathautoLocaleTestCase extends PathautoFunctionalTestHelper {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Pathauto localization tests',
|
||||
'description' => 'Test pathauto functionality with localization and translation.',
|
||||
'group' => 'Pathauto',
|
||||
'dependencies' => array('token'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp(array $modules = array()) {
|
||||
$modules[] = 'locale';
|
||||
$modules[] = 'translation';
|
||||
parent::setUp($modules, array('administer languages'));
|
||||
|
||||
// Add predefined French language and reset the locale cache.
|
||||
require_once DRUPAL_ROOT . '/includes/locale.inc';
|
||||
locale_add_language('fr', NULL, NULL, LANGUAGE_LTR, '', 'fr');
|
||||
drupal_language_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that when an English node is updated, its old English alias is
|
||||
* updated and its newer French alias is left intact.
|
||||
*/
|
||||
function testLanguageAliases() {
|
||||
$node = array(
|
||||
'title' => 'English node',
|
||||
'language' => 'en',
|
||||
'body' => array('en' => array(array())),
|
||||
'path' => array(
|
||||
'alias' => 'english-node',
|
||||
'pathauto' => FALSE,
|
||||
),
|
||||
);
|
||||
$node = $this->drupalCreateNode($node);
|
||||
$english_alias = path_load(array('alias' => 'english-node', 'language' => 'en'));
|
||||
$this->assertTrue($english_alias, 'Alias created with proper language.');
|
||||
|
||||
// Also save a French alias that should not be left alone, even though
|
||||
// it is the newer alias.
|
||||
$this->saveEntityAlias('node', $node, 'french-node', 'fr');
|
||||
|
||||
// Add an alias with the soon-to-be generated alias, causing the upcoming
|
||||
// alias update to generate a unique alias with the '-0' suffix.
|
||||
$this->saveAlias('node/invalid', 'content/english-node', LANGUAGE_NONE);
|
||||
|
||||
// Update the node, triggering a change in the English alias.
|
||||
$node->path['pathauto'] = TRUE;
|
||||
pathauto_node_update($node);
|
||||
|
||||
// Check that the new English alias replaced the old one.
|
||||
$this->assertEntityAlias('node', $node, 'content/english-node-0', 'en');
|
||||
$this->assertEntityAlias('node', $node, 'french-node', 'fr');
|
||||
$this->assertAliasExists(array('pid' => $english_alias['pid'], 'alias' => 'content/english-node-0'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk update functionality tests.
|
||||
*/
|
||||
class PathautoBulkUpdateTestCase extends PathautoFunctionalTestHelper {
|
||||
private $nodes;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Pathauto bulk updating',
|
||||
'description' => 'Tests bulk updating of URL aliases.',
|
||||
'group' => 'Pathauto',
|
||||
'dependencies' => array('token'),
|
||||
);
|
||||
}
|
||||
|
||||
function testBulkUpdate() {
|
||||
// Create some nodes.
|
||||
$this->nodes = array();
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->nodes[$node->nid] = $node;
|
||||
}
|
||||
|
||||
// Clear out all aliases.
|
||||
$this->deleteAllAliases();
|
||||
|
||||
// Bulk create aliases.
|
||||
$edit = array(
|
||||
'update[node_pathauto_bulk_update_batch_process]' => TRUE,
|
||||
'update[user_pathauto_bulk_update_batch_process]' => TRUE,
|
||||
);
|
||||
$this->drupalPost('admin/config/search/path/update_bulk', $edit, t('Update'));
|
||||
$this->assertText('Generated 7 URL aliases.'); // 5 nodes + 2 users
|
||||
|
||||
// Check that aliases have actually been created.
|
||||
foreach ($this->nodes as $node) {
|
||||
$this->assertEntityAliasExists('node', $node);
|
||||
}
|
||||
$this->assertEntityAliasExists('user', $this->admin_user);
|
||||
|
||||
// Add a new node.
|
||||
$new_node = $this->drupalCreateNode(array('path' => array('alias' => '', 'pathauto' => FALSE)));
|
||||
|
||||
// Run the update again which should only run against the new node.
|
||||
$this->drupalPost('admin/config/search/path/update_bulk', $edit, t('Update'));
|
||||
$this->assertText('Generated 1 URL alias.'); // 1 node + 0 users
|
||||
|
||||
$this->assertEntityAliasExists('node', $new_node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Token functionality tests.
|
||||
*/
|
||||
class PathautoTokenTestCase extends PathautoFunctionalTestHelper {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Pathauto tokens',
|
||||
'description' => 'Tests tokens provided by Pathauto.',
|
||||
'group' => 'Pathauto',
|
||||
'dependencies' => array('token'),
|
||||
);
|
||||
}
|
||||
|
||||
function testPathautoTokens() {
|
||||
$array = array(
|
||||
'test first arg',
|
||||
'The Array / value',
|
||||
);
|
||||
|
||||
$tokens = array(
|
||||
'join-path' => 'test-first-arg/array-value',
|
||||
);
|
||||
$data['array'] = $array;
|
||||
$replacements = $this->assertTokens('array', $data, $tokens);
|
||||
|
||||
// Ensure that the pathauto_clean_token_values() function does not alter
|
||||
// this token value.
|
||||
module_load_include('inc', 'pathauto');
|
||||
pathauto_clean_token_values($replacements, $data, array());
|
||||
$this->assertEqual($replacements['[array:join-path]'], 'test-first-arg/array-value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Function copied from TokenTestHelper::assertTokens().
|
||||
*/
|
||||
function assertTokens($type, array $data, array $tokens, array $options = array()) {
|
||||
$input = $this->mapTokenNames($type, array_keys($tokens));
|
||||
$replacements = token_generate($type, $input, $data, $options);
|
||||
foreach ($tokens as $name => $expected) {
|
||||
$token = $input[$name];
|
||||
if (!isset($expected)) {
|
||||
$this->assertTrue(!isset($values[$token]), t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
|
||||
}
|
||||
elseif (!isset($replacements[$token])) {
|
||||
$this->fail(t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
|
||||
}
|
||||
elseif (!empty($options['regex'])) {
|
||||
$this->assertTrue(preg_match('/^' . $expected . '$/', $replacements[$token]), t("Token value for @token was '@actual', matching regular expression pattern '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $replacements[$token], '@expected' => $expected)));
|
||||
}
|
||||
else {
|
||||
$this->assertIdentical($replacements[$token], $expected, t("Token value for @token was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $replacements[$token], '@expected' => $expected)));
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
function mapTokenNames($type, array $tokens = array()) {
|
||||
$return = array();
|
||||
foreach ($tokens as $token) {
|
||||
$return[$token] = "[$type:$token]";
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Token integration for the Pathauto module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function pathauto_token_info() {
|
||||
$info = array();
|
||||
|
||||
$info['tokens']['array']['join-path'] = array(
|
||||
'name' => t('Joined path'),
|
||||
'description' => t('The array values each cleaned by Pathauto and then joined with the slash into a string that resembles an URL.'),
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function pathauto_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$replacements = array();
|
||||
|
||||
if ($type == 'array' && !empty($data['array'])) {
|
||||
$array = $data['array'];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'join-path':
|
||||
module_load_include('inc', 'pathauto');
|
||||
$values = array();
|
||||
foreach (element_children($array) as $key) {
|
||||
$value = is_array($array[$key]) ? render($array[$key]) : (string) $array[$key];
|
||||
$value = pathauto_cleanstring($value);
|
||||
$values[] = $value;
|
||||
}
|
||||
$replacements[$original] = implode('/', $values);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
Reference in New Issue
Block a user