first import
This commit is contained in:
339
sites/all/modules/features_override/LICENSE.txt
Normal file
339
sites/all/modules/features_override/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.
|
167
sites/all/modules/features_override/README.txt
Normal file
167
sites/all/modules/features_override/README.txt
Normal file
@@ -0,0 +1,167 @@
|
||||
Features Override (Drupal 7-2.x)
|
||||
--------------------------------
|
||||
|
||||
This module add a new Features exportable called "Feature Overrides" that
|
||||
are used to export overridden changes to other Features. It requires at
|
||||
least the 7.x-1.0-rc1 release of Features.
|
||||
|
||||
To use, install this module and enable it. When you create a new feature from
|
||||
the Structure/Features page, two new exportables called "Feature Overrides"
|
||||
will be displayed in the drop-down list. The first one allows you to override
|
||||
all of the changes of a specific component. The second "Individual Advanced"
|
||||
allows you to select exactly which line-by-line changes are exported.
|
||||
Select the Overrode exportable and then select which components you wish to
|
||||
export overrides for. Only components that are currently overridden will be
|
||||
shown as checkboxes.
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
- mpotter (Mike Potter)
|
||||
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
1) Create normal features and enable them.
|
||||
|
||||
2) Make changes to the site using the normal Drupal UI.
|
||||
|
||||
3) Go to the admin/structure/features page and you should see some of your
|
||||
Features marked as "Overridden"
|
||||
|
||||
4) Click the "Create Feature" tab.
|
||||
|
||||
5) Enter a name for your Override feature.
|
||||
|
||||
6) Click the "Edit components" drop-down and select "Feature Overrides".
|
||||
A list of overridden components will be shown. For example, if you changed a
|
||||
field in a content type, that field name will be shown in the list of
|
||||
overrides. If you changed something in a view, that view name will be in the
|
||||
list. Check the boxes next to the overrides you wish to save.
|
||||
|
||||
7) Click the "Download Feature" button at the bottom of the screen. This will
|
||||
create your Override Feature module code and save it to a local file. Upload
|
||||
that file to your server and place it into your normal sites/all/modules
|
||||
directory.
|
||||
|
||||
8) Go to the Modules page and Enable your new override module.
|
||||
|
||||
9) Clear the Drupal cache.
|
||||
|
||||
10) Now when you visit the admin/structure/features you should see your new
|
||||
override feature and the original features should no longer be marked as
|
||||
"Overridden".
|
||||
|
||||
|
||||
Merging new changes into an existing Override
|
||||
---------------------------------------------
|
||||
Once you have created an Override feature, it's easy to add additional changes
|
||||
to it:
|
||||
|
||||
1) Make changes to the site via the Drupal UI
|
||||
|
||||
2) Visit admin/structure/features and you should see both the original code
|
||||
feature marked as "Overridden" as well as the Override feature marked as
|
||||
"Overridden"
|
||||
|
||||
3) Click the Recreate link for the Override feature.
|
||||
|
||||
4) Select any new overrides from the Component dropdown list as needed.
|
||||
Download your new feature.
|
||||
|
||||
You can accomplish this same task using Drush:
|
||||
|
||||
drush features-update override-feature
|
||||
|
||||
5) Now visit the Features admin page and nothing should be marked as Overridden
|
||||
again.
|
||||
|
||||
NOTE: You want to update/recreate the Override feature and NOT the original
|
||||
feature. If you recreate the original feature, then ALL of the overrides (the
|
||||
existing ones in the Override module and the new changes) will be written to
|
||||
the original feature. Probably not what you wanted (see next section)
|
||||
|
||||
Rebuilding the Original Feature without the Overrides
|
||||
-----------------------------------------------------
|
||||
Sometimes you want to make a change and have that change saved with the
|
||||
original feature and not with the Override. Here are the steps to accomplish
|
||||
this:
|
||||
|
||||
1) Make the changes you need to the site via the Drupal UI
|
||||
|
||||
2) Visit admin/structure/features and you should see both the original code
|
||||
feature marked as "Overridden" as well as the Override feature marked as
|
||||
"Overridden"
|
||||
|
||||
3) Click the "Create Feature" tab to create a new feature
|
||||
|
||||
4) Create a new Override feature by entering a name and description, then
|
||||
select the overrides you want to save from the Feature Override section of the
|
||||
Components drop-down menu
|
||||
|
||||
5) Click Download Feature and install this new module on your site. Let's
|
||||
call it "New Changes". So now we have the "Original Feature", the first
|
||||
"Override Feature", and the new "New Changes" feature. All three should
|
||||
display in the Features Admin page in their Default state.
|
||||
|
||||
6) From the Features Admin page, uncheck the "New Changes" feature you created
|
||||
in step 5, then click Save. This will undo the recent changes.
|
||||
|
||||
7) From the Features Admin page, uncheck the box next to the "Override Feature"
|
||||
that you originally created (NOT the New one you made in step 5) and click
|
||||
Save. This will undo the changes made by the first Override module.
|
||||
|
||||
8) If the original feature shows as "Overridden" or "Needs Review", click on
|
||||
it and click the Revert button to ensure it is in it's original state.
|
||||
|
||||
9) From the Features Admin page, check the box next to the "New Changes"
|
||||
feature you created in step 5 to enable is and click Save. Now the database
|
||||
reflects the original feature plus the new changes.
|
||||
|
||||
10) Click the Recreate link for the original Feature. Click the Download link
|
||||
and install the updated feature. Or use the drush command:
|
||||
"drush features-update original-feature". This will export the original
|
||||
feature code along with the New Changes code.
|
||||
|
||||
11) You no longer need the New Changes feature. You can disable it and remove
|
||||
it from your site if you wish. If you don't remove it completely, at least
|
||||
ensure that it is disabled in the Feature Admin page.
|
||||
|
||||
12) Finally, check the box next to the Override feature to re-enable that
|
||||
feature. Now you have the original code plus the New changes stored in the
|
||||
original feature, but you still have the additional Overrides in the seperate
|
||||
Override module.
|
||||
|
||||
Once you understand the above steps you will also realize that there are other
|
||||
ways to accomplish this same task. For example, you could have disabled the
|
||||
Override module first, then made your changes and just recreated the original
|
||||
feature directly. However, the above procedure is the most complete and
|
||||
reflects the real-life situation where the changes have already been made to
|
||||
the site and you need to somehow capture those changes back into the original
|
||||
feature.
|
||||
|
||||
Adding or Removing specific Override lines
|
||||
------------------------------------------
|
||||
An Override feature is simply a list of code changes that need to be made to
|
||||
the current configuration. Only code *differences* are stored in the Override
|
||||
feature.
|
||||
|
||||
To view these specific line-by-line code differences, click the Default link
|
||||
next to your Override module from the Features admin page, then click the
|
||||
Review Overrides tab. This will show the Overrides currently exported as
|
||||
individual lines (along with the normal "diff" listing below).
|
||||
|
||||
To change which specific lines are exported, click the Recreate tab, then
|
||||
open the Components dropdown. Select the "Features Overrides Individual"
|
||||
(advanced) tab. Then click the "Refine" link next to the component you want
|
||||
to adjust. Each specific override line will be shown as a checkbox. Simply
|
||||
check or uncheck the lines desired. Then click the Download button to create
|
||||
a new version of your Override feature.
|
||||
|
||||
In the main Features Admin page there is also a new Review Overrides tab.
|
||||
This will show a list of any new overrides no matter which module that relate
|
||||
to. This is a very useful debugging tool for determining where changes have
|
||||
been made to your site. The Overrides tab will tell you the exact Component
|
||||
being overridden. The normal "Review Overrides" tab in Features only shows the
|
||||
raw code "diffs" and sometimes cannot show the full context of the change. The
|
||||
new Review Overrides tab can show you exactly what the change is and where it
|
||||
is made (which View changed, which field changed, etc).
|
122
sites/all/modules/features_override/features_override.admin.inc
Normal file
122
sites/all/modules/features_override/features_override.admin.inc
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Page callbacks features overrides admin pages.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders the differences for a component and element
|
||||
*/
|
||||
function features_override_render_differences($component = '', $element = '', $module = '') {
|
||||
module_load_include('inc', 'features_override', 'features_override.export');
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
drupal_add_css(drupal_get_path('module', 'features_override') . '/features_override.css');
|
||||
if (empty($module)) {
|
||||
$differences = features_override_get_overrides($component, $element, TRUE, !empty($component));
|
||||
}
|
||||
else {
|
||||
// only grab the differences for the specific module we are interested in
|
||||
$differences = array();
|
||||
foreach ($module->info['features'] as $comp_name => $comp) {
|
||||
if ($difference = array_filter(features_override_module_component_overrides($module->name, $comp_name, FALSE, TRUE))) {
|
||||
$differences[$comp_name] = isset($differences[$comp_name]) ? array_merge($difference, $differences[$comp_name]) : $difference;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$differences || !count(array_filter($differences))) {
|
||||
return t('No overrides were found.');
|
||||
}
|
||||
if (!empty($element)) {
|
||||
$differences = array($element => $differences);
|
||||
}
|
||||
if (!empty($component)) {
|
||||
$differences = array($component => $differences);
|
||||
}
|
||||
$rows = array();
|
||||
foreach ($differences as $component_name => $item) {
|
||||
$code_change = array();
|
||||
$code_add = array();
|
||||
$code_delete = array();
|
||||
foreach ($item as $element_name => $difference) {
|
||||
$key = (isset($_GET['key'])) ? $_GET['key'] : '';
|
||||
if (!empty($key)) {
|
||||
if (!empty($difference['additions'][$key])) {
|
||||
return '<pre>' . check_plain(implode("\n",
|
||||
features_override_features_export_render_addition($difference['additions'][$key], $element_name, $component_name))) .
|
||||
'</pre>';
|
||||
}
|
||||
elseif (!empty($difference['deletions'][$key])) {
|
||||
return '<pre>' . check_plain(implode("\n",
|
||||
features_override_features_export_render_deletion($difference['deletions'][$key], $element_name, $component_name))) .
|
||||
'</pre>';
|
||||
}
|
||||
else {
|
||||
return t('Unable to find referenced information.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!empty($difference['additions'])) {
|
||||
foreach ($difference['additions'] as $alter) {
|
||||
if (is_scalar($alter['value'])) {
|
||||
$code_change[] = array('<pre>' . check_plain(implode("\n", features_override_features_export_render_addition($alter, $element_name, $component_name))). '</pre>');
|
||||
}
|
||||
else {
|
||||
$code_add[] = array('<pre>' . check_plain(implode("\n", features_override_features_export_render_addition($alter, $element_name, $component_name, FALSE))). '</pre>');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($difference['deletions'])) {
|
||||
foreach ($difference['deletions'] as $alter) {
|
||||
$code_delete[] = array('<pre>' . check_plain(implode("\n", features_override_features_export_render_deletion($alter, $element_name, $component_name))). '</pre>');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($code_add) || count($code_delete) || count($code_change)) {
|
||||
$rows[] = array(array('data' => $component_name, 'header' => TRUE));
|
||||
}
|
||||
if (count($code_change)) {
|
||||
$rows[] = array('CHANGES');
|
||||
$rows = array_merge($rows, $code_change);
|
||||
}
|
||||
if (count($code_add)) {
|
||||
$rows[] = array('ADDITIONS');
|
||||
$rows = array_merge($rows, $code_add);
|
||||
}
|
||||
if (count($code_delete)) {
|
||||
$rows[] = array('DELETIONS');
|
||||
$rows = array_merge($rows, $code_delete);
|
||||
}
|
||||
}
|
||||
return theme('table', array('rows' => $rows, 'attributes' => array('class' => 'features_override_table')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Page callback to display the differences between what's in code and
|
||||
* what is in the db.
|
||||
*
|
||||
* @param $feature
|
||||
* A loaded feature object to display differences for.
|
||||
* @param $component
|
||||
* Optional: specific component to display differences for. If excluded, all components are used.
|
||||
*
|
||||
* @return Themed display of what is different.
|
||||
*/
|
||||
function features_override_feature_diff($feature, $component = NULL) {
|
||||
if (module_exists('diff')) {
|
||||
module_load_include('inc', 'features', 'features.admin');
|
||||
$output = features_feature_diff($feature, $component);
|
||||
$output['diff'] = array(
|
||||
'#prefix' => '<h2>',
|
||||
'#markup' => 'Code diff',
|
||||
'#suffix' => '</h2>',
|
||||
'#weight' => -98,
|
||||
);
|
||||
}
|
||||
$output['overrides'] = array(
|
||||
'#markup' => features_override_render_differences('', '', $feature),
|
||||
'#weight' => -99,
|
||||
);
|
||||
return $output;
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Autogenerated hook that duplicates what alters are being exported via
|
||||
* features overrides.
|
||||
*
|
||||
* This hook should only ever be auto-exported.
|
||||
*/
|
||||
function hook_features_override_default() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows modules to clean up the default and normal components.
|
||||
*
|
||||
* For whatever reason, extra information or inconstancies may be introduced
|
||||
* into the normal (current) or default (defined) components. This hook allows
|
||||
* modules to hook in and clean up whatever they need to.
|
||||
*
|
||||
* @param $default
|
||||
* The object or array as defined in a default hook, unaltered.
|
||||
* @param $normal
|
||||
* The current object, either the current default + alters or database
|
||||
* overrides.
|
||||
* @param $context
|
||||
* an array containing module and component information.
|
||||
*/
|
||||
function hook_features_override_component_overrides_alter(&$default, &$normal, $context) {
|
||||
if ($context['component'] == 'views_view') {
|
||||
unset($normal->api_version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component hook. The hook should be implemented using the name ot the
|
||||
* component, not the module, eg. [component]_features_export() rather than
|
||||
* [module]_features_export().
|
||||
*
|
||||
* Renders an addition to a feature.
|
||||
*
|
||||
* @return string
|
||||
* A rendered string.
|
||||
* @see features_override_export_render_addition();
|
||||
*/
|
||||
function hook_features_override_export_render_addition() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Component hook. The hook should be implemented using the name ot the
|
||||
* component, not the module, eg. [component]_features_export() rather than
|
||||
* [module]_features_export().
|
||||
*
|
||||
* Renders a deletion to a feature.
|
||||
*
|
||||
* @return string
|
||||
* A rendered string.
|
||||
* @see features_override_export_render_deletion();
|
||||
*/
|
||||
function hook_features_override_export_render_deletion() {
|
||||
}
|
45
sites/all/modules/features_override/features_override.css
Normal file
45
sites/all/modules/features_override/features_override.css
Normal file
@@ -0,0 +1,45 @@
|
||||
div.form-item.form-type-checkbox {
|
||||
padding: 0;
|
||||
}
|
||||
table.features_override_table td {
|
||||
padding: 4px 10px;
|
||||
}
|
||||
table.features_override_table td pre {
|
||||
margin: 0 0;
|
||||
line-height: 150%;
|
||||
}
|
||||
#edit-sources-features-overrides {
|
||||
overflow-y: visible;
|
||||
height: auto;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
#edit-sources-features-overrides h4 {
|
||||
background: #E1E2DC;
|
||||
border: 1px solid #BEBFB9;
|
||||
margin: 6px 0;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
}
|
||||
a.features_override_button {
|
||||
color: #333;
|
||||
background: #EEE;
|
||||
padding: 0px 5px;
|
||||
margin-right: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
float: right;
|
||||
}
|
||||
#edit-sources-features-overrides h4 a {
|
||||
color: #333;
|
||||
background: #CCC;
|
||||
margin-right: 0;
|
||||
}
|
||||
a.features_override_button:hover {
|
||||
background: #DEF;
|
||||
}
|
||||
#edit-sources-features-overrides h4 a.features_override_button:hover {
|
||||
background: #DEF;
|
||||
}
|
||||
#edit-sources-features-overrides div.form-type-checkbox {
|
||||
clear: right;
|
||||
}
|
571
sites/all/modules/features_override/features_override.export.inc
Normal file
571
sites/all/modules/features_override/features_override.export.inc
Normal file
@@ -0,0 +1,571 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Helper function to export features overrides.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses the identifier into indivudal parts.
|
||||
*
|
||||
* As the keys may have a period in them, cannot use explode or similair ways.
|
||||
*
|
||||
* @param $identifier
|
||||
* A string in the form <comonent>.<element>.<keys> or <component>.<element>.
|
||||
* @return
|
||||
* An array of component, element, and keys string
|
||||
* @see features_override_make_key()
|
||||
*/
|
||||
function features_override_parse_identifier($identifier) {
|
||||
$first_period = strpos($identifier, '.');
|
||||
$component = substr($identifier, 0, $first_period);
|
||||
if ($second_period = strpos($identifier, '.', $first_period + 1)) {
|
||||
$element = substr($identifier, $first_period + 1, $second_period - $first_period - 1);
|
||||
$keys = substr($identifier, $second_period + 1);
|
||||
}
|
||||
else {
|
||||
$element = substr($identifier, $first_period + 1);
|
||||
$keys = FALSE;
|
||||
}
|
||||
return array($component, $element, $keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a distinct string key from an array of keys.
|
||||
*
|
||||
* @param $keys
|
||||
* An array of keys.
|
||||
* @return
|
||||
* A string representation of the keys.
|
||||
*/
|
||||
function features_override_make_key($keys) {
|
||||
if (is_array($keys)) {
|
||||
$return_keys = array();
|
||||
foreach ($keys as $key) {
|
||||
$return_keys[] = $key['key'];
|
||||
}
|
||||
return implode('|', $return_keys);
|
||||
}
|
||||
else {
|
||||
return $keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of keys to be ignored for various exportables
|
||||
* @param $component
|
||||
* The component to retrieve ignore_keys from.
|
||||
*/
|
||||
function features_get_ignore_keys($component) {
|
||||
static $cache;
|
||||
if (!isset($cache[$component])) {
|
||||
$cache[$component] = module_invoke_all('features_override_ignore', $component);
|
||||
}
|
||||
return $cache[$component];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculautes what overrides exist for by component/element.
|
||||
*
|
||||
* @param $component_key
|
||||
* A component key that's defined via hook_features_api.
|
||||
* @param $element_key
|
||||
* A key identifieing an element that's been overriden.
|
||||
* @param $reset
|
||||
* Reset the internal cache of overrides gathered.
|
||||
* @param $all
|
||||
* If TRUE, return all overrides, otherwise only overrides not yet in an override feature
|
||||
* */
|
||||
function features_override_get_overrides($component_key = FALSE, $element_key = FALSE, $reset = FALSE, $all = TRUE) {
|
||||
static $cache;
|
||||
if (!isset($cache) || $reset) {
|
||||
$cache = array();
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
features_include();
|
||||
foreach (features_get_components() as $component => $info) {
|
||||
if (empty($info['default_hook']) || $component == 'features_override_items' || $component == 'features_overrides' || !features_get_default_alter_hook($component) | !features_hook($component, 'features_export_render')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
features_include_defaults($component);
|
||||
foreach (module_implements($info['default_hook']) as $module) {
|
||||
if ($differences = array_filter(features_override_module_component_overrides($module, $component, $reset, $all))) {
|
||||
$cache[$component] = isset($cache[$component]) ? array_merge($differences, $cache[$component]) : $differences;
|
||||
}
|
||||
}
|
||||
$cache[$component] = isset($cache[$component]) ? array_filter($cache[$component]) : array();
|
||||
}
|
||||
}
|
||||
|
||||
if ($component_key && $element_key) {
|
||||
return !empty($cache[$component_key][$element_key]) ? $cache[$component_key][$element_key] : array();
|
||||
}
|
||||
elseif ($component_key) {
|
||||
return !empty($cache[$component_key]) ? $cache[$component_key] : array();
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get overrides for specific module/component.
|
||||
*
|
||||
* @param $module
|
||||
* An enabled module to find overrides for it's components.
|
||||
* @param $component
|
||||
* A type of component to find overrides for.
|
||||
* @param $reset
|
||||
* Reset the internal cache of overrides gathered.
|
||||
* @param $all
|
||||
* If TRUE, return all overrides, otherwise only overrides not yet in an override feature
|
||||
* @return
|
||||
* An array of overrides found.
|
||||
*/
|
||||
function features_override_module_component_overrides($module, $component, $reset = FALSE, $all = TRUE) {
|
||||
static $cache = array();
|
||||
|
||||
if (isset($cache[$module][$component]) && !$reset) {
|
||||
return $cache[$module][$component];
|
||||
}
|
||||
module_load_include('inc', 'features_override', 'features_override.hooks');
|
||||
features_include();
|
||||
features_include_defaults($component);
|
||||
|
||||
// Allows overriding non-feature controlled code.
|
||||
$default_hook = features_get_default_hooks($component);
|
||||
if ($all) {
|
||||
// call hooks directly
|
||||
// could also do
|
||||
// $default = features_get_default($component, $module, FALSE, $reset);
|
||||
// but this is more efficient
|
||||
$default = module_invoke($module, $default_hook);
|
||||
}
|
||||
else {
|
||||
$default = features_get_default($component, $module, TRUE, $reset);
|
||||
}
|
||||
$normal = features_get_normal($component, $module, $reset);
|
||||
|
||||
// This indicates it is likely not controlled by features, so fetch manually.
|
||||
if (!$normal && is_array($default)) {
|
||||
$code = array_pop(features_invoke($component, 'features_export_render', $module, array_keys($default), NULL));
|
||||
if (!$code) {
|
||||
return FALSE;
|
||||
}
|
||||
else {
|
||||
$normal = eval($code);
|
||||
}
|
||||
}
|
||||
|
||||
$context = array(
|
||||
'component' => $component,
|
||||
'module' => $module,
|
||||
);
|
||||
|
||||
// Can't use _features_sanitize as that resets some keys.
|
||||
_features_override_sanitize($normal);
|
||||
_features_override_sanitize($default);
|
||||
// make a deep copy of data to prevent problems when removing recursion later
|
||||
$default_copy = unserialize(serialize($default));
|
||||
$normal_copy = unserialize(serialize($normal));
|
||||
|
||||
$ignore_keys = features_get_ignore_keys($component);
|
||||
// remove keys to be ignored
|
||||
// doing this now allows us to better control which recursive parts are removed
|
||||
if (count($ignore_keys)) {
|
||||
_features_override_remove_ignores($default_copy, $ignore_keys);
|
||||
_features_override_remove_ignores($normal_copy, $ignore_keys);
|
||||
}
|
||||
// now remove any remaining recursion
|
||||
features_override_remove_recursion($default_copy);
|
||||
features_override_remove_recursion($normal_copy);
|
||||
|
||||
$component_overrides = array();
|
||||
if ($normal && is_array($normal) || is_object($normal)) {
|
||||
foreach ($normal as $name => $properties) {
|
||||
$component_overrides[$name] = array(
|
||||
'additions' => array(),
|
||||
'deletions' => array(),
|
||||
);
|
||||
if (isset($default_copy[$name])) {
|
||||
drupal_alter('features_override_component_overrides', $default_copy[$name], $normal_copy[$name], $context);
|
||||
_features_override_set_additions($default_copy[$name], $normal_copy[$name], $component_overrides[$name]['additions'], $ignore_keys);
|
||||
_features_override_set_deletions($default_copy[$name], $normal_copy[$name], $component_overrides[$name]['deletions'], $ignore_keys);
|
||||
}
|
||||
if (!array_filter($component_overrides[$name])) {
|
||||
$component_overrides[$name] = FALSE;
|
||||
}
|
||||
}
|
||||
// now check for any elements that are in $default but not in $normal that we didn't process yet
|
||||
foreach ($default as $name => $properties) {
|
||||
if (!isset($normal_copy[$name])) {
|
||||
$_keys = array(array('type' => 'array', 'key' => $name));
|
||||
$component_overrides[$name]['deletions'][features_override_make_key($name)] = array(
|
||||
'keys' => $name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
$cache[$module][$component] = $component_overrides;
|
||||
return $component_overrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array by its keys (assoc) or values (non-assoc).
|
||||
*
|
||||
* @param $array
|
||||
* An array that needs to be sorted.
|
||||
*/
|
||||
function _features_override_sanitize(&$array) {
|
||||
if (is_array($array)) {
|
||||
$is_assoc = (array_keys($array) !== range(0, count($array) - 1));
|
||||
if ($is_assoc) {
|
||||
ksort($array);
|
||||
}
|
||||
else {
|
||||
sort($array);
|
||||
}
|
||||
foreach ($array as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
_features_override_sanitize($array[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the additions between default and normal features.
|
||||
*
|
||||
* @param $default
|
||||
* The default defination of a component.
|
||||
* @param $normal
|
||||
* The current defination of a component.
|
||||
* @param $additions
|
||||
* An array of currently gathered additions.
|
||||
* @param $ignore_keys
|
||||
* Keys to ignore while processing element.
|
||||
* @param $level
|
||||
* How many levels deep into object.
|
||||
* @param $keys
|
||||
* The keys for this level.
|
||||
*/
|
||||
function _features_override_set_additions(&$default, &$normal, &$additions, $ignore_keys = array(), $level = 0, $keys = array()) {
|
||||
$object = is_object($normal);
|
||||
foreach ($normal as $key => $value) {
|
||||
if (isset($ignore_keys[$key]) && ($level == $ignore_keys[$key])) {
|
||||
continue;
|
||||
}
|
||||
if ($object) {
|
||||
if (!is_object($default) || !property_exists($default, $key) || (is_scalar($value) && ($default->$key !== $value))) {
|
||||
$_keys = array_merge($keys, array(array('type' => 'object', 'key' => $key)));
|
||||
$additions[features_override_make_key($_keys)] = array(
|
||||
'keys' => $_keys,
|
||||
'value' => $value,
|
||||
'original' => (is_scalar($value) && isset($default->$key)) ? $default->$key : '',
|
||||
);
|
||||
}
|
||||
elseif (property_exists($default, $key) && ($default->$key !== $value)) {
|
||||
_features_override_set_additions($default->$key, $value, $additions, $ignore_keys, $level+1, array_merge($keys, array(array('type' => 'object', 'key' => $key))));
|
||||
}
|
||||
}
|
||||
elseif (is_array($normal)) {
|
||||
if (!is_array($default) || !array_key_exists($key, $default) || (is_scalar($value) && ($default[$key] !== $value))) {
|
||||
$_keys = array_merge($keys, array(array('type' => 'array', 'key' => $key)));
|
||||
$additions[features_override_make_key($_keys)] = array(
|
||||
'keys' => $_keys,
|
||||
'value' => $value,
|
||||
'original' => (is_scalar($value) && isset($default[$key])) ? $default[$key] : '',
|
||||
);
|
||||
}
|
||||
elseif (array_key_exists($key, $default) && (!is_null($value) && ($default[$key] !== $value))) {
|
||||
_features_override_set_additions($default[$key], $value, $additions, $ignore_keys, $level+1, array_merge($keys, array(array('type' => 'array', 'key' => $key))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the deletions between default and normal features.
|
||||
*
|
||||
* @param $default
|
||||
* The default defination of a component.
|
||||
* @param $normal
|
||||
* The current defination of a component.
|
||||
* @param $deletions
|
||||
* An array of currently gathered deletions.
|
||||
* @param $ignore_keys
|
||||
* Keys to ignore while processing element.
|
||||
* @param $level
|
||||
* How many levels deep into object.
|
||||
* @param $keys
|
||||
* The keys for this level.
|
||||
*/
|
||||
function _features_override_set_deletions(&$default, &$normal, &$deletions, $ignore_keys = array(), $level = 0, $keys = array()) {
|
||||
$object = is_object($default);
|
||||
|
||||
foreach ($default as $key => $value) {
|
||||
if (isset($ignore_keys[$key]) && ($level == $ignore_keys[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($object) {
|
||||
if (!property_exists($normal, $key)) {
|
||||
$_keys = array_merge($keys, array(array('type' => 'object', 'key' => $key)));
|
||||
$deletions[features_override_make_key($_keys)] = array(
|
||||
'keys' => $_keys,
|
||||
);
|
||||
}
|
||||
elseif (property_exists($normal, $key) && (is_array($value) || is_object($value))) {
|
||||
_features_override_set_deletions($value, $normal->$key, $deletions, $ignore_keys, $level+1, array_merge($keys, array(array('type' => 'object', 'key' => $key))));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!array_key_exists($key, $normal)) {
|
||||
$_keys = array_merge($keys, array(array('type' => 'array', 'key' => $key)));
|
||||
$deletions[features_override_make_key($_keys)] = array(
|
||||
'keys' => $_keys,
|
||||
);
|
||||
}
|
||||
elseif (array_key_exists($key, $normal) && (is_array($value) || is_object($value))) {
|
||||
_features_override_set_deletions($value, $normal[$key], $deletions, $ignore_keys, $level+1, array_merge($keys, array(array('type' => 'array', 'key' => $key))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of an array of keys.
|
||||
*
|
||||
* @param $keys
|
||||
* An array of keys with their associate types.
|
||||
*
|
||||
* @return
|
||||
* A string representation of the keys.
|
||||
*/
|
||||
function features_override_export_keys($keys) {
|
||||
$line = '';
|
||||
if (is_array($keys)) {
|
||||
foreach ($keys as $key) {
|
||||
if ($key['type'] == 'object') {
|
||||
$line .= "->{$key['key']}";
|
||||
}
|
||||
else {
|
||||
$line .= "['{$key['key']}']";
|
||||
}
|
||||
}
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes recursion from an object or array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
*/
|
||||
function features_override_remove_recursion(&$item) {
|
||||
_features_override_remove_recursion($item);
|
||||
_features_override_remove_recursion_markers($item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to removes recursion from an object/array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
*/
|
||||
function _features_override_remove_recursion(&$item) {
|
||||
$is_object = is_object($item);
|
||||
if ($is_object) {
|
||||
$item->{FEATURES_OVERRIDE_RECURSION_MARKER} = 1;
|
||||
}
|
||||
else {
|
||||
$item[FEATURES_OVERRIDE_RECURSION_MARKER] = 1;
|
||||
}
|
||||
foreach ($item as $key => $value) {
|
||||
if (is_array($value) || is_object($value)) {
|
||||
$remove = is_array($value) ? !empty($value[FEATURES_OVERRIDE_RECURSION_MARKER]) : !empty($value->{FEATURES_OVERRIDE_RECURSION_MARKER});
|
||||
if ($remove) {
|
||||
if ($is_object) {
|
||||
unset($item->$key);
|
||||
}
|
||||
else {
|
||||
unset($item[$key]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
features_override_remove_recursion($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to removes recursion from an object/array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
*/
|
||||
function _features_override_remove_recursion_markers(&$item) {
|
||||
$is_object = is_object($item);
|
||||
foreach ($item as $key => $value) {
|
||||
if ($key === FEATURES_OVERRIDE_RECURSION_MARKER) {
|
||||
if ($is_object) {
|
||||
unset($item->$key);
|
||||
}
|
||||
else {
|
||||
unset($item[$key]);
|
||||
}
|
||||
}
|
||||
elseif (is_array($value) || is_object($value)) {
|
||||
_features_override_remove_recursion_markers($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to removes a set of keys an object/array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
* @param $ignore_keys
|
||||
* Array of keys to be ignored. Values are the level of the key.
|
||||
* @param $level
|
||||
* Level of key to remove. Up to 2 levels deep because $item can still be
|
||||
* recursive
|
||||
*/
|
||||
function _features_override_remove_ignores(&$item, $ignore_keys, $level = -1) {
|
||||
$is_object = is_object($item);
|
||||
foreach ($item as $key => $value) {
|
||||
if (isset($ignore_keys[$key]) && ($ignore_keys[$key] == $level)) {
|
||||
if ($is_object) {
|
||||
unset($item->$key);
|
||||
}
|
||||
else {
|
||||
unset($item[$key]);
|
||||
}
|
||||
}
|
||||
elseif (($level < 2) && (is_array($value) || is_object($value))) {
|
||||
_features_override_remove_ignores($value, $ignore_keys, $level+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drupal-friendly var_export().
|
||||
*
|
||||
* @param $var
|
||||
* The variable to export.
|
||||
* @param $prefix
|
||||
* A prefix that will be added at the beginning of every lines of the output.
|
||||
* @return
|
||||
* The variable exported in a way compatible to Drupal's coding standards.
|
||||
*/
|
||||
function features_override_var_export($var, $prefix = '') {
|
||||
if (is_array($var) || is_object($var)) {
|
||||
// Special causing array so calls features_override_var_export instead of
|
||||
// features_var_export.
|
||||
if (is_array($var)) {
|
||||
if (empty($var)) {
|
||||
$output = 'array()';
|
||||
}
|
||||
else {
|
||||
$output = "array(\n";
|
||||
foreach ($var as $key => $value) {
|
||||
// Using normal var_export on the key to ensure correct quoting.
|
||||
$output .= " " . var_export($key, TRUE) . " => " . features_override_var_export($value, ' ', FALSE) . ",\n";
|
||||
}
|
||||
$output .= ')';
|
||||
}
|
||||
}
|
||||
// Objects do not export cleanily.
|
||||
else {
|
||||
if (method_exists($var, 'export')) {
|
||||
$output = $var->export();
|
||||
}
|
||||
elseif (get_class($var) === 'stdClass') {
|
||||
$output = '(object) ' . features_override_var_export((array) $var, $prefix);
|
||||
}
|
||||
elseif (!method_exists($var, '__set_state')) {
|
||||
// Ugly, but custom object with no clue how to export.without
|
||||
// __set_state class and var_export produces unusable code.
|
||||
$output = 'unserialize(' . var_export(serialize($var), TRUE) . ')';
|
||||
}
|
||||
else {
|
||||
$output = var_export($var, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
$output = features_var_export($var);
|
||||
}
|
||||
|
||||
if ($prefix) {
|
||||
$output = str_replace("\n", "\n$prefix", $output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the addition/change to an element.
|
||||
*/
|
||||
function features_override_features_export_render_addition($alter, $element, $component, $is_change = TRUE) {
|
||||
module_load_include('inc', 'features_override', 'features_override.hooks');
|
||||
if (features_hook($component, 'features_override_export_render_addition')) {
|
||||
return features_invoke($component, 'features_override_export_render_addition', $alter, $element);
|
||||
}
|
||||
else {
|
||||
$code = array();
|
||||
$component_start = "\$data['$element']";
|
||||
$code_line = features_override_export_keys($alter['keys']);
|
||||
$value_export = features_override_var_export($alter['value'], ' ');
|
||||
if ($is_change) {
|
||||
$original_export = (isset($alter['original'])) ? ' /* WAS: ' . features_override_var_export($alter['original'], ' ') . ' */' : '';
|
||||
}
|
||||
else {
|
||||
$original_export = '';
|
||||
}
|
||||
$code[] = " " . $component_start . $code_line . ' = ' . $value_export . ';' . $original_export;
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the deletion to an element.
|
||||
*/
|
||||
function features_override_features_export_render_deletion($alter, $element, $component) {
|
||||
module_load_include('inc', 'features_override', 'features_override.hooks');
|
||||
if (features_hook($component, 'features_override_export_render_deletion')) {
|
||||
return features_invoke($component, 'features_override_export_render_deletion', $alter, $element);
|
||||
}
|
||||
else {
|
||||
$code = array();
|
||||
$component_start = "\$data['$element']";
|
||||
$code_line = features_override_export_keys($alter['keys']);
|
||||
$code[] = ' unset(' . $component_start . $code_line . ');';
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a string for use as option.
|
||||
|
||||
* @see features_dom_encode_options()
|
||||
* @param $string
|
||||
* A string to encode.
|
||||
* @return
|
||||
* An encoded string for use as option value.
|
||||
*/
|
||||
function features_override_encode_string($string) {
|
||||
$replacements = array(
|
||||
':' => '__'. ord(':') .'__',
|
||||
'/' => '__'. ord('/') .'__',
|
||||
',' => '__'. ord(',') .'__',
|
||||
'.' => '__'. ord('.') .'__',
|
||||
'<' => '__'. ord('<') .'__',
|
||||
'>' => '__'. ord('>') .'__',
|
||||
);
|
||||
return strtr($string, $replacements);
|
||||
}
|
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Page callback for features override pages.
|
||||
*/
|
||||
|
||||
module_load_include('inc', 'features_override', 'features_override.export');
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function features_override_items_features_export_options() {
|
||||
$options = $links = array();
|
||||
$overrides = features_override_get_overrides();
|
||||
foreach ($overrides as $component => $elements) {
|
||||
foreach ($elements as $key => $element) {
|
||||
$options["{$component}.{$key}"] = "{$component} {$key} ";
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function features_overrides_features_export_options() {
|
||||
$options = $sub_links = $main_links = array();
|
||||
drupal_add_js(drupal_get_path('module', 'features_override') . '/features_override_form.js');
|
||||
drupal_add_css(drupal_get_path('module', 'features_override') . '/features_override.css');
|
||||
|
||||
$overrides = features_override_get_overrides();
|
||||
foreach ($overrides as $component => $elements) {
|
||||
foreach ($elements as $key => $element) {
|
||||
$identifier = "{$component}.{$key}";
|
||||
$identifier_spaced = "{$component} {$key} ";
|
||||
$main_links[features_override_encode_string($identifier)] = url('admin/structure/features/features_override/' . $component . '/' . $key);
|
||||
if (!empty($element['additions'])) {
|
||||
foreach ($element['additions'] as $change_key => $changes) {
|
||||
$options[$identifier . '.' . $change_key] = $identifier_spaced . ' addition: of ' . features_override_export_keys($changes['keys']);
|
||||
$sub_links[features_override_encode_string($identifier . '.' . $change_key)] = url('admin/structure/features/features_override/' . $component . '/' . $key, array('query' => array('key' => $change_key)));
|
||||
}
|
||||
}
|
||||
if (!empty($element['deletions'])) {
|
||||
foreach ($element['deletions'] as $change_key => $changes) {
|
||||
$options[$identifier . '.' . $change_key] = $identifier_spaced . ' deletion of ' . features_override_export_keys($changes['keys']);
|
||||
$sub_links[features_override_encode_string($identifier . '.' . $change_key)] = url('admin/structure/features/features_override/' . $component . '/' . $key, array('query' => array('key' => $change_key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drupal_add_js(array('features_override_links' => array('main' => $main_links, 'sub' => $sub_links)), 'setting');
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function features_override_items_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array('features_overrides' => array());
|
||||
$map = features_get_default_map('features_override_items');
|
||||
$map_overrides = features_get_default_map('features_overrides');
|
||||
static $options;
|
||||
if (!isset($options)) {
|
||||
$options = features_overrides_features_export_options();
|
||||
}
|
||||
|
||||
foreach ($data as $identifier) {
|
||||
// If this override bundle is already provided by another module, remove the field
|
||||
// and add the other module as a dependency.
|
||||
if (isset($map[$identifier]) && $map[$identifier] != $module_name) {
|
||||
if (isset($export['features']['features_override_items'][$identifier])) {
|
||||
unset($export['features']['features_override_items'][$identifier]);
|
||||
}
|
||||
$module = $map[$identifier];
|
||||
$export['dependencies'][$module] = $module;
|
||||
}
|
||||
// If the field has not yet been exported, add it
|
||||
else {
|
||||
$export['features']['features_override_items'][$identifier] = $identifier;
|
||||
list($component, $element) = features_override_parse_identifier($identifier);
|
||||
|
||||
// Add in all current overrides to import.
|
||||
foreach ($options as $option_key => $option_name) {
|
||||
list($options_component, $options_element, $options_keys) = features_override_parse_identifier($option_key);
|
||||
if ($options_element == $element && empty($map_overrides[$option_key])) {
|
||||
$pipe['features_overrides'][] = $option_key;
|
||||
}
|
||||
}
|
||||
|
||||
// Add in depedency to module that defines this item.
|
||||
$component_map = features_get_default_map($component);
|
||||
if (!empty($component_map[$element])) {
|
||||
$export['dependencies'][$component_map[$element]] = $component_map[$element];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function features_overrides_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array('features_overrides' => array());
|
||||
$map = features_get_default_map('features_overrides');
|
||||
static $options;
|
||||
|
||||
foreach ($data as $identifier) {
|
||||
// If this override bundle is already provided by another module, remove the field
|
||||
// and add the other module as a dependency.
|
||||
if (isset($map[$identifier]) && $map[$identifier] != $module_name) {
|
||||
if (isset($export['features']['features_overrides'][$identifier])) {
|
||||
unset($export['features']['features_overrides'][$identifier]);
|
||||
}
|
||||
$module = $map[$identifier];
|
||||
$export['dependencies'][$module] = $module;
|
||||
}
|
||||
// If the field has not yet been exported, add it
|
||||
else {
|
||||
$export['features']['features_overrides'][$identifier] = $identifier;
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function features_override_items_features_export_render($module, $data, $export = NULL) {
|
||||
// no code is needed for this exportable. Details are stored via features_overrides_features_export_render
|
||||
$list = array();
|
||||
|
||||
/*
|
||||
// Go through all data collecting the items exports to create.
|
||||
$features_override_code = array(
|
||||
' // This code is only used to work nicely with features UI.',
|
||||
' $overrides = array();',
|
||||
);
|
||||
foreach ($data as $identifier) {
|
||||
$features_override_code[] = '';
|
||||
$features_override_code[] = " // Exported overrides for: $identifier";
|
||||
$features_override_code[] = ' $overrides[' . features_var_export($identifier) . '] = 1;';
|
||||
}
|
||||
|
||||
// Create the default hook that defines all exported overrides.
|
||||
$features_override_code[] = '';
|
||||
$features_override_code[] = ' return $overrides;';
|
||||
$list['features_override_default_items'] = implode("\n", $features_override_code);
|
||||
*/
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function features_overrides_features_export_render($module, $data, $export = NULL) {
|
||||
// Remember, the code exported here is just for the Features UI to keep track
|
||||
// of the overridden status. All it needs to do is capture "changes".
|
||||
// The actual form of the output doesn't really matter.
|
||||
$list = $overrides_to_export = array();
|
||||
|
||||
// Go through all data collecting the items exports to create.
|
||||
$features_override_code = array(
|
||||
' // This code is only used for UI in features. Exported alters hooks do the magic.',
|
||||
' $overrides = array();',
|
||||
);
|
||||
$last_component = '';
|
||||
foreach ($data as $identifier) {
|
||||
// Something is adding extra quote marks.
|
||||
list($component, $element, $keys) = features_override_parse_identifier($identifier);
|
||||
$component_code[$component] = isset($component_code[$component]) ? $component_code[$component] : array();
|
||||
$overrides_to_export[$component] = isset($overrides_to_export[$component]) ? $overrides_to_export[$component] : array();
|
||||
$overrides = features_override_get_overrides($component);
|
||||
$features_override_value = array(
|
||||
'additions' => array(),
|
||||
'deletions' => array()
|
||||
);
|
||||
// Just specific keys are being exported so add them if set.
|
||||
if (!empty($overrides[$element])) {
|
||||
if (isset($overrides[$element]['additions'][$keys])) {
|
||||
$overrides_to_export[$component][$element]['additions'][] = $overrides[$element]['additions'][$keys];
|
||||
$features_override_value['additions'][] = $overrides[$element]['additions'][$keys];
|
||||
}
|
||||
elseif (isset($overrides[$element]['deletions'][$keys])) {
|
||||
$overrides_to_export[$component][$element]['deletions'][] = $overrides[$element]['deletions'][$keys];
|
||||
$features_override_value['deletions'][] = $overrides[$element]['deletions'][$keys];
|
||||
}
|
||||
}
|
||||
if ($component != $last_component) {
|
||||
$features_override_code[] = '';
|
||||
$features_override_code[] = " // Exported overrides for: $component";
|
||||
}
|
||||
$last_component = $component;
|
||||
if (count($features_override_value['additions'])) {
|
||||
$features_override_code[] = ' $overrides["' . $identifier . '"] = ' . features_override_var_export($features_override_value['additions'][0]['value'], ' ') . ';';
|
||||
}
|
||||
else if (count($features_override_value['deletions'])) {
|
||||
$features_override_code[] = ' $overrides["' . $identifier . '"]["DELETED"] = TRUE;';
|
||||
}
|
||||
}
|
||||
|
||||
// Create the default hook that defines all exported overrides.
|
||||
$features_override_code[] = '';
|
||||
$features_override_code[] = ' return $overrides;';
|
||||
$list['features_override_default_overrides'] = implode("\n", $features_override_code);
|
||||
|
||||
$component_code = array();
|
||||
foreach ($overrides_to_export as $component => $elements) {
|
||||
foreach ($elements as $element => $overrides) {
|
||||
$component_code[$component] = isset($component_code[$component]) ? $component_code[$component] : array();
|
||||
$component_code[$component][] = ' if (isset($data[' . var_export($element,TRUE) . '])) {';
|
||||
if (!empty($overrides['additions'])) {
|
||||
foreach ($overrides['additions'] as $alter) {
|
||||
$component_code[$component][] = implode("\n", features_override_features_export_render_addition($alter, $element, $component));
|
||||
}
|
||||
}
|
||||
if (!empty($overrides['deletions'])) {
|
||||
foreach ($overrides['deletions'] as $alter) {
|
||||
$component_code[$component][] = implode("\n", features_override_features_export_render_deletion($alter, $element, $component));
|
||||
}
|
||||
}
|
||||
$component_code[$component][] = ' }';
|
||||
}
|
||||
}
|
||||
|
||||
$info = features_get_components();
|
||||
foreach ($component_code as $component => $code) {
|
||||
$list[features_get_default_alter_hook($component) . '_alter'] = array(
|
||||
'code' => implode("\n", $code),
|
||||
'args' => '&$data',
|
||||
'file' => 'features',
|
||||
);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function features_override_items_features_revert($module) {
|
||||
features_override_items_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements of hook_features_rebuild().
|
||||
*/
|
||||
function features_override_items_features_rebuild($module) {
|
||||
_features_override_features_rebuild($module, 'features_override_items');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function features_overrides_features_revert($module) {
|
||||
features_overrides_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements of hook_features_rebuild().
|
||||
*/
|
||||
function features_overrides_features_rebuild($module) {
|
||||
_features_override_features_rebuild($module, 'features_overrides');
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds fields from code defaults.
|
||||
*
|
||||
* FIXME This is reverting everything that has a change instead of this module.
|
||||
*/
|
||||
function _features_override_features_rebuild($module, $component_key = 'features_override_items') {
|
||||
// first build list of components we are overriding
|
||||
$parents = array();
|
||||
$data = features_get_component_map();
|
||||
foreach ($data[$component_key] as $identifier => $component_modules) {
|
||||
list($component, $element) = features_override_parse_identifier($identifier);
|
||||
if (isset($data[$component][$element])) {
|
||||
foreach ($data[$component][$element] as $module_name) {
|
||||
$parents[$module][$component] = $module_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
// first, update get_default cache so rebuild will work on latest data
|
||||
foreach ($parents as $module => $element) {
|
||||
foreach ($element as $component) {
|
||||
features_get_default($component, $module, TRUE, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
// rebuild those parent components
|
||||
//features_rebuild($parents);
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains implementation of features_override's hooks for other modules.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_features_override_component_overrides_alter().
|
||||
*/
|
||||
function image_features_override_component_overrides_alter(&$default, &$normal, $context) {
|
||||
if ($context['component'] == 'image') {
|
||||
// Effects have their keys changed on override. This resets them to numeric.
|
||||
$normal['effects'] = array_values($normal['effects']);
|
||||
$default['effects'] = array_values($default['effects']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_override_export_render_addition() for image.
|
||||
*/
|
||||
function image_features_override_export_render_addition($alter, $element) {
|
||||
$code = array();
|
||||
if (isset($alter['keys']) && isset($alter['value'])) {
|
||||
$component_start = "\$data['$element']";
|
||||
$code_line = features_override_export_keys($alter['keys']);
|
||||
$value_export = features_override_var_export($alter['value'], ' ');
|
||||
$code[] = "";
|
||||
$code[] = " if (" . $component_start . "['storage'] == IMAGE_STORAGE_DEFAULT) {";
|
||||
$code[] = " " . $component_start . $code_line . ' = ' . $value_export . ';';
|
||||
$code[] = " }";
|
||||
}
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_override_export_render_addition() for image.
|
||||
*/
|
||||
function image_features_override_export_render_deletion($alter, $element) {
|
||||
$code = array();
|
||||
if (isset($alter['keys'])) {
|
||||
$component_start = "\$data['$element']";
|
||||
$code_line = features_override_export_keys($alter['keys']);
|
||||
$code[] = "";
|
||||
$code[] = " if (" . $component_start . "['storage'] == IMAGE_STORAGE_DEFAULT) {";
|
||||
$code[] = ' unset(' . $component_start . $code_line . ');';
|
||||
$code[] = " }";
|
||||
}
|
||||
return $code;
|
||||
}
|
13
sites/all/modules/features_override/features_override.info
Normal file
13
sites/all/modules/features_override/features_override.info
Normal file
@@ -0,0 +1,13 @@
|
||||
name = Features Override
|
||||
description = Allows exported Features to be overridden
|
||||
core = 7.x
|
||||
dependencies[] = ctools
|
||||
dependencies[] = features
|
||||
package = "Features"
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-07-25
|
||||
version = "7.x-2.0-beta1+3-dev"
|
||||
core = "7.x"
|
||||
project = "features_override"
|
||||
datestamp = "1343175314"
|
||||
|
126
sites/all/modules/features_override/features_override.module
Normal file
126
sites/all/modules/features_override/features_override.module
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Module file for features overrides.
|
||||
* Includes core/contrib hook implementations.
|
||||
*/
|
||||
|
||||
// Key to use when marking properties for recursion.
|
||||
define('FEATURES_OVERRIDE_RECURSION_MARKER', 'features_override_recursion_marker');
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function features_override_features_api() {
|
||||
$path = drupal_get_path('module', 'features_override') . '/features_override.features.inc';
|
||||
return array(
|
||||
'features_override_items' => array(
|
||||
'name' => t('Feature Overrides'),
|
||||
'default_hook' => 'features_override_default_items',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => TRUE,
|
||||
'file' => $path,
|
||||
),
|
||||
'features_overrides' => array(
|
||||
'name' => t('Feature Overrides (individual -- advanced)'),
|
||||
'default_hook' => 'features_override_default_overrides',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => TRUE,
|
||||
'file' => $path,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function features_override_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['admin/structure/features/features_override'] = array(
|
||||
'title' => 'Review Overrides',
|
||||
'description' => 'Show override details for a component and element.',
|
||||
'page callback' => 'features_override_render_differences',
|
||||
'access callback' => 'user_access',
|
||||
'access arguments' => array('administer features'),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => "features_override.admin.inc",
|
||||
'weight' => 8,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_alter
|
||||
*/
|
||||
function features_override_menu_alter(&$items) {
|
||||
// override the existing "Review Overrides" tab
|
||||
// do it here instead of hook_menu to ensure we run after features
|
||||
$items['admin/structure/features/%feature/diff'] = array(
|
||||
'title' => 'Review overrides',
|
||||
'description' => 'Compare default and current feature.',
|
||||
'page callback' => 'features_override_feature_diff',
|
||||
'page arguments' => array(3, 5),
|
||||
'load arguments' => array(3, TRUE),
|
||||
'access callback' => 'features_access_override_actions',
|
||||
'access arguments' => array(3),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'features_override.admin.inc',
|
||||
'module' => 'features_override',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_override_ignore().
|
||||
*/
|
||||
function features_override_features_override_ignore($component) {
|
||||
// Determine which keys need to be ignored for override diff for various components.
|
||||
// value is shows how many levels deep the key is
|
||||
$ignores = array();
|
||||
switch ($component) {
|
||||
case 'views_view':
|
||||
$ignores['current_display'] = 0;
|
||||
$ignores['display_handler'] = 0;
|
||||
$ignores['handler'] = 2;
|
||||
$ignores['query'] = 0;
|
||||
$ignores['localization_plugin'] = 0;
|
||||
// Views automatically adds these two on export to set values.
|
||||
$ignores['api_version'] = 0;
|
||||
$ignores['disabled'] = 0;
|
||||
break;
|
||||
case 'image':
|
||||
$ignores['module'] = 0;
|
||||
$ignores['name'] = 0;
|
||||
$ignores['storage'] = 0;
|
||||
// Various properities are loaded into the effect in image_styles.
|
||||
$ignores['summary theme'] = 2;
|
||||
$ignores['module'] = 2;
|
||||
$ignores['label'] = 2;
|
||||
$ignores['help'] = 2;
|
||||
$ignores['form callback'] = 2;
|
||||
$ignores['effect callback'] = 2;
|
||||
$ignores['dimensions callback'] = 2;
|
||||
break;
|
||||
case 'field':
|
||||
$ignores['locked'] = 1;
|
||||
break;
|
||||
}
|
||||
return $ignores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_modules_enabled().
|
||||
*
|
||||
* Revert the parent settings when the override module is enabled.
|
||||
*/
|
||||
function features_override_modules_enabled($modules) {
|
||||
module_load_include('inc', 'features_override', 'features_override.features');
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
foreach ($modules as $module) {
|
||||
if (function_exists($module . '_features_override_default')) {
|
||||
features_override_features_rebuild($module);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
(function ($) {
|
||||
Drupal.behaviors.features_override_form = {
|
||||
attach: function(context, settings) {
|
||||
$('#edit-sources-features-overrides:not(.features-override-processed)', context)
|
||||
.prepend(Drupal.t('Advanced usage only. Allows you to select individual changes only to export.'))
|
||||
.addClass('features-override-processed');
|
||||
|
||||
$('input[type=checkbox][name^="sources[features_override_items]"]:not(.features-override-form-processed)', context).each(function (i) {
|
||||
var $parent_checkbox = $(this);
|
||||
$parent_checkbox.addClass('features-override-form-processed');
|
||||
var $parent_label = $parent_checkbox.parent().find('label');
|
||||
// Create a link that links to the exact differences from the label.
|
||||
if (Drupal.settings.features_override_links['main'][this.value]) {
|
||||
$parent_label.append('<a href="' + Drupal.settings.features_override_links['main'][this.value] + '" target="_blank" class="features_override_button">' + Drupal.t('view') + '</a>');
|
||||
}
|
||||
|
||||
var $child_checkboxes = $('input[type=checkbox][name^="sources[features_overrides]"][value^=' + this.value + ']').each(function (i) {
|
||||
if (Drupal.settings.features_override_links['sub'][this.value]) {
|
||||
$($(this).parent()).find('label').append('<a href="' + Drupal.settings.features_override_links['sub'][this.value] + '" target="_blank" class="features_override_button">' + Drupal.t('view') + '</a>');
|
||||
}
|
||||
}).parents('div.form-type-checkbox');
|
||||
$child_checkboxes.wrapAll('<div class="features-override-children-wrapper" id="' + this.id + '-wrapper"></div>');
|
||||
var $wrapper = $child_checkboxes.parent();
|
||||
|
||||
// Prepend a label saying what these overrides are for.
|
||||
$wrapper.before('<h4>' + Drupal.t('Individual overrides for: ') + $parent_label.html() + '</h4>');
|
||||
var fotext = Drupal.t('Full overrides already exported for this item to this feature.')
|
||||
+ ' '
|
||||
+ '<a href="#" id="' + this.id + '-refine" class="features_override_button">' + Drupal.t('refine') + '</a>';
|
||||
$wrapper.after('<div class="features-override-children-warning" id="' + this.id + '-warning">' + fotext + '</div>');
|
||||
|
||||
// Unchecks the items component to allow indivudal refinment if desired.
|
||||
$('#' + this.id + '-refine').bind('click', {id : this.id}, function(event) {
|
||||
$('#' + event.data.id).removeAttr('checked').trigger('change');
|
||||
return false;
|
||||
});
|
||||
|
||||
// Disable child checkboxes when parent is selected
|
||||
$parent_checkbox.change(function() {
|
||||
features_override_switch_child_info(this.id, this.checked);
|
||||
});
|
||||
features_override_switch_child_info(this.id, this.checked);
|
||||
});
|
||||
}
|
||||
}
|
||||
function features_override_switch_child_info(id, is_checked) {
|
||||
if (is_checked) {
|
||||
// Jquery bug that hide() doesn't always work when parent hidden.
|
||||
$('#' + id + '-wrapper').css('display', 'none');
|
||||
$('#' + id + '-warning').css('display', 'block');
|
||||
}
|
||||
else {
|
||||
$('#' + id + '-wrapper').show();
|
||||
$('#' + id + '-warning').css('display', 'none');
|
||||
}
|
||||
}
|
||||
})(jQuery);
|
Reference in New Issue
Block a user