FINAL suepr merge step : added all modules to this super repos
This commit is contained in:
189
sites/all/modules/contrib/admin/features/API.txt
Normal file
189
sites/all/modules/contrib/admin/features/API.txt
Normal file
@@ -0,0 +1,189 @@
|
||||
|
||||
Features 1.x API for Drupal 7.x
|
||||
-------------------------------
|
||||
This file contains documentation for two audiences: site builders interested in
|
||||
creating and managing features and module developers interested in integrating
|
||||
with the features module.
|
||||
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
- A **feature module** is a Drupal module that contains the `feature` key in its
|
||||
`.info` file. This array describes the components that should be managed by
|
||||
Features within the module.
|
||||
|
||||
- A **component** is a configuration object that can be exported. Examples: a
|
||||
view, content type or CCK field instance.
|
||||
|
||||
- A **machine name** is a string identifier for a specific type of component and
|
||||
should be unique within a single Drupal site. It should not be a numerically
|
||||
generated serial id.
|
||||
|
||||
- An **exportable** component is one that can be used solely from a default hook
|
||||
in code and without an instance in the database. For example, a view that lives
|
||||
in code does not need an entry in the database in order to be used.
|
||||
|
||||
- A **faux-exportable** component is one that must exist in the database in
|
||||
order to be used. Any exports of this component are used to create or
|
||||
synchronize entries in the database that share the same machine name.
|
||||
|
||||
|
||||
### Component states
|
||||
|
||||
Features provides some infrastructure to determine the state of components on
|
||||
the site. To determine the state of a component Features keeps track of three
|
||||
things using an md5 hash of
|
||||
|
||||
- current code for the component. This is the configuration as actually
|
||||
represented in code by a given feature.
|
||||
|
||||
- the most recent prior code state that differs from the current code state. For
|
||||
example, if an `svn update` changes the configuration of a view, this stores the
|
||||
code state *prior* to the update.
|
||||
|
||||
- The "normal" component state. This is the configuration represented by the
|
||||
component as stored in the database or the default component (with any changes
|
||||
introduced by `drupal_alter()`) if no database override exists.
|
||||
|
||||
Using these three values, Features determines a component to be in one of the
|
||||
following five states:
|
||||
|
||||
- **Default** (`FEATURES_DEFAULT`) The object has no database entry or the
|
||||
database entry matches the state of the component in code. This should be the
|
||||
default state of components after installing a feature. Updating the component
|
||||
can be done by altering the code definition directly.
|
||||
|
||||
- **Overridden** (`FEATURES_OVERRIDDEN`) The code remains constant but the
|
||||
database object does not match the state of the component in code. Changes must
|
||||
be reverted before the component can be updated from code.
|
||||
|
||||
- **Needs review** (`FEATURES_NEEDS_REVIEW`) The previous code state, database
|
||||
state, and current code state all differ. This occurs most commonly when a user
|
||||
changes one of her components and then pulls updates to her codebase. Since
|
||||
there is no way to tell whether the code state or the database state is more
|
||||
recent/valid, user input is necessary to resolve this state.
|
||||
|
||||
- **Rebuildable** (`FEATURES_REBUILDABLE`) This state only applies to
|
||||
**faux-exportables** and indicates that the database component must and can be
|
||||
safely updated from the code definition. The database entry does not match the
|
||||
current code state but does match the previous code state. Features assumes that
|
||||
in this scenario the user has made no substantive changes and the component can
|
||||
be updated automatically.
|
||||
|
||||
- **Rebuilding** (`FEATURES_REBUILDING`) This state is rarely seen and only
|
||||
applies to **faux-exportables.** This state is shown when a
|
||||
`FEATURES_REBUILDABLE` component is *currently* being synced to the database.
|
||||
Usually this operation is very fast and short lived. However, if the operation
|
||||
is interrupted (e.g. the server goes down) this state will be seen until the
|
||||
rebuild locking semaphore is cleared.
|
||||
|
||||
|
||||
How a feature is generated
|
||||
--------------------------
|
||||
At a high level Features writes the code in a feature module using the following
|
||||
steps:
|
||||
|
||||
1. An `.info` file describing the components that should be included in a
|
||||
Feature is generated. It is either read from an existing feature or generated
|
||||
through the Features UI.
|
||||
|
||||
2. Features converts the info file into an `$export` array which contains a list
|
||||
of elements to be exported. Each component type is given a chance to add to the
|
||||
export list as well as request that *other* components be given a second chance
|
||||
to add to the `$export` array.
|
||||
|
||||
3. If any additional components have been queued up in the `$pipe` we repeat
|
||||
step 2 for each of the queued component types.
|
||||
|
||||
4. Once a full `$export` array is populated each component renders items from
|
||||
the `$export` array to PHP code as a exportable defaults hook.
|
||||
|
||||
5. Finally, Features writes the code into files and delivers it as a
|
||||
downloadable package (UI) or writes it directly to a module directory (drush).
|
||||
|
||||
This workflow makes a variety of things possible:
|
||||
|
||||
### Add components to a feature
|
||||
|
||||
Add the components to the `features` array in the feature's `.info` file and run
|
||||
`drush features-update`. The same operation can be performed using the
|
||||
*Recreate* page in the Features UI.
|
||||
|
||||
### Remove components from a feature
|
||||
|
||||
Remove the corresponding component lines from the feature's `.info` file and run
|
||||
`drush features-update`. The same operation can be performed using the
|
||||
*Recreate* page in the Features UI.
|
||||
|
||||
### Rename a component
|
||||
|
||||
Rename a component by changing its name in the feature's `.info` file and the
|
||||
key and name property of the exported object in the appropriate `.inc` file in
|
||||
the feature. Note that any references in other configuration objects to the
|
||||
previous name should also be updated.
|
||||
|
||||
|
||||
Integrating your module with the Features API
|
||||
---------------------------------------------
|
||||
This section is for developers interested in adding Features-based management
|
||||
for the configuration objects in their modules. From the perspective of
|
||||
Features, there are a few different ways that modules store their configuration:
|
||||
|
||||
- In the `variable` table using `variable_set()`. If a module is using variables
|
||||
for storing configuration, these variable settings can be exported with Features
|
||||
by using the [Strongarm][1] module.
|
||||
|
||||
**Features integration:** Install the Strongarm module.
|
||||
|
||||
- Using a custom table with a serial ID for identifying configuration objects.
|
||||
If this is the case, you will need to change your schema to use a string
|
||||
identifier / machine name for each object.
|
||||
|
||||
**Features integration:** Fix your schema first, then see below.
|
||||
|
||||
- Using a custom table with a machine name identifier and custom exportables
|
||||
handling (e.g. you have your own defaults hook handling and export generation).
|
||||
If this is the case, you will need to implement many of the features hooks
|
||||
yourself.
|
||||
|
||||
**Features integration:** `hook_features_api()`, `hook_features_export()`,
|
||||
`hook_features_export_render()`, `hook_features_export_options()`,
|
||||
`hook_features_revert()`.
|
||||
|
||||
- Using a custom table with CTools Export API integration. If this is the case,
|
||||
Features will automatically have integration with your module. You can implement
|
||||
any of the Features hooks in order to override the default CTools exportables
|
||||
integration behavior.
|
||||
|
||||
**Features integration:** Automatically provided. You may implement any of the
|
||||
Features hooks where you need further customization for your configuration
|
||||
object.
|
||||
|
||||
If it isn't clear by now, we highly recommend using the [CTools][2] Export API
|
||||
for adding exportables to your module. Stella has written a [fantastic HOWTO][3]
|
||||
on using the CTools Export API that can get you started.
|
||||
|
||||
|
||||
An overview of Features hooks
|
||||
-----------------------------
|
||||
Extensive documentation of the hooks provided by Features is available in
|
||||
`features.api.php`. This section provides a short overview of each hook and its
|
||||
role.
|
||||
|
||||
- `hook_features_api()` defines one or more component types that are available
|
||||
to Features for export and a variety of settings for each type.
|
||||
- `hook_features_export()` processes a list of components, detecting any
|
||||
dependencies or further components
|
||||
- `hook_features_export_options()` provides an array of components that can be
|
||||
exported for a given type.
|
||||
- `hook_features_export_render()` renders a set of components to code as a
|
||||
defaults hook.
|
||||
- `hook_features_revert()` reverts components of a feature back to their default
|
||||
state.
|
||||
- `hook_features_rebuild()` updates faux-exportable components back to their
|
||||
default state. Only applies to faux-exportables.
|
||||
|
||||
|
||||
[1]: http://drupal.org/project/strongarm
|
||||
[2]: http://drupal.org/project/ctools
|
||||
[3]: http://civicactions.com/blog/2009/jul/24/using_chaos_tools_module_create_exportables
|
221
sites/all/modules/contrib/admin/features/CHANGELOG.txt
Normal file
221
sites/all/modules/contrib/admin/features/CHANGELOG.txt
Normal file
@@ -0,0 +1,221 @@
|
||||
features DRUPAL-7--1-0
|
||||
----------------------
|
||||
|
||||
#1647894 by tim.plunkett: Fixed Features with page manager components are perpetually overridden.
|
||||
#1635662 by donquixote: Fixed Undefined index: feature in features_get_info().
|
||||
Add missing taxonomy test file
|
||||
|
||||
features DRUPAL-7--1-0-RC3
|
||||
--------------------------
|
||||
|
||||
#1429262 by joelcollinsdc, neochief: Added Allow component level reverting (without needing to go to interactive mode) in drush fr.
|
||||
#1272586 by alexweber | stevector: Added Increment or directly set version number directly with drush features-update.
|
||||
#1532422 by nadavoid: Fixed If the custom sort order of 'format_handlers()' is not maintained, some things can break.
|
||||
#1564864 by jeffschuler: Fixed Mixup in hook_features_export_render() example.
|
||||
#1567506 by Dave Reid: Fixed Unable to properly export or provide a locked field in a feature.
|
||||
#1597792 by jessehs: Make sure all comments in created features end period.
|
||||
#1599188 by jessehs: Move inline comment for empty feature .module file to @file doc block.
|
||||
#1587200 by ericduran: Fixed Test module should be hidden.
|
||||
#1574716 by bojanz: Fixed Avoid unnecessary cache rebuilds when creating and updating fields.
|
||||
#1530386 by exratione, bojanz, kotnik: Avoid unnecessary cache rebuilds and improve installation performance.
|
||||
#1489480 by Xen | dema502: Fixed drush:features-export Ambiguous component.
|
||||
#1159390 by hefox | brad.bulger: Fixed incorrect revert hook name in features.api.php?.
|
||||
#1493274 by hefox, neochief: Fixed Feature installed before Strongarm blocks all other variable imports.
|
||||
#1537838 by hefox, JvE | Gisle: Fixed Upgrading to 7.x-1.0-rc2 (from rc1) breaks taxonomy creation.
|
||||
#1447656 by bcmiller0, smk-ka: Added drush features-revert-all performance improvement.
|
||||
|
||||
features DRUPAL-7--1-0-RC2
|
||||
--------------------------
|
||||
|
||||
#1529202 by mpotter, mstrelan: Fixed Unable to revert overrides because clear-block is now clearfix.
|
||||
#1505044 by neochief: Fixed features_var_export() adds additional prefix to multiline strings contained in objects.
|
||||
#1510710 by neochief: Fixed Incorrect key in features_include_defaults() with reset parameters.
|
||||
#1402262 by hefox: Fixed features.api.php mentions 'features_source()' key, but the actual key in use is 'feature_source()'.
|
||||
#939254 by hefox, fago: Added Return the () to hook_features_pipe_component_alter().
|
||||
#894572 by pearcec, eporama, Grayside, andrewlevine: Added Features with only module dependencies not recognized as features.
|
||||
#1231118 by tim.plunkett, jhedstrom, nielsonm, arnested: Fixed Generate code according to coding standards.
|
||||
#1493274 by neochief, hefox: Fixed Feature installed before Strongarm blocks all other variable imports.
|
||||
#1287594 by tim.plunkett: Don't wrap .info file values in double quotes.
|
||||
#1437388 by hefox: Fixed check if file exists before including it in features_include().
|
||||
#1496322 by hefox, dajjen: Added Make package autocomplete search string case insensitive .
|
||||
#1279938 by derhasi, helmo: Added Features support for languages.
|
||||
#1305048 by mongolito404: Fixed Undefined index error in user_permission_features_export_render().
|
||||
#1063204 by careernerd, davidwhthomas, hefox | mrfree: Fixed Adding a new permission causes integrity constraint violation.
|
||||
#1432264 by ezra-g, tim.plunkett | samhassell: Fixed Changes to hook_views_api() cause Views plugins to be undefined.
|
||||
Revert "Issue #1432264 by ezra-g, tim.plunkett | samhassell: Fixed Changes to hook_views_api() cause Views plugins to be undefined."
|
||||
|
||||
features DRUPAL-7--1-0-RC1
|
||||
--------------------------
|
||||
|
||||
#1331278 by Xen, tim.plunkett, joelcollinsdc, mpotter: Drush features-add and features-export are confusingly simi
|
||||
#1475780 by rggoode: Added Prevent starting features machine name starting with numeric.
|
||||
#1402826 by mpotter: Added Make 'Checking...' a link.
|
||||
#1478808 by mpotter: Added Why is Description field required?.
|
||||
#1279938 by derhasi, helmo: Added Features support for languages.
|
||||
#1009900 by DamienMcKenna: Check if a variable is empty before comparing it to a known value.
|
||||
#1426452 by mpotter, rypit: Added Conflicts with disabled modules should be colored differently.
|
||||
#984472 by hefox, goron, Sarenc, mpotter: Added hook_node_info_alter(), and alter_type(), alter_hook() to features
|
||||
#1478808 by mpotter: Added Why is Description field required?.
|
||||
#1479068 by mpotter: Added Allow exportable code written to different files.
|
||||
#1058778 by dajjen | nymo: Added ability to edit package of feature when creating/editing features via the UI.
|
||||
#1429408 by joelcollinsdc: Fixed Diff page does not show component titles.
|
||||
#1264826 by Volx, hefox | mortendk: Fixed revert views calls views delete() & throws a fatal error .
|
||||
#1432264 by ezra-g, tim.plunkett | samhassell: Fixed Changes to hook_views_api() cause Views plugins to be undefin
|
||||
#1479068 by mpotter: Added Allow exportable code written to different files.
|
||||
#1437370 by hefox: Fixed . is being mapped to ord(',') instead of ord('.') in features_dom_encode_options()/featur
|
||||
Added a README section about security concerns
|
||||
Updated some docs, specifically the maintainers
|
||||
#1231118 by jhedstrom: Generate code according to coding standards when using include_once.
|
||||
#1305194 by hefox,Xen,mpotter: Provide support for exporting of altering of components
|
||||
#1305194 by smk-ka,jief: Taxonomy reference field export recursion
|
||||
#1390848: Fixing a slight bug from the previous patch that lead to wrong components being shown as being able to b
|
||||
#1390848 by hefox: Centralize the call to hook_features_api()
|
||||
Updated the features_test Feature
|
||||
#1399440 by hefox: Fixed Feature tests failure on image against drupal 7.10.
|
||||
|
||||
features DRUPAL-7--1-0-BETA6
|
||||
----------------------------
|
||||
|
||||
#1382156 by tim.plunkett | davidn: Fixed PHP Fatal error: Call to undefined function ctools_features_declare_funct
|
||||
|
||||
features DRUPAL-7--1-0-BETA5
|
||||
----------------------------
|
||||
|
||||
#1363284 by tim.plunkett, mpotter: Fixed Drush --force option is declared wrong.
|
||||
#1079440 by mpotter, mrf, tobby, acrollet, donquixote: Fixed Module name check prevents panels custom content pane
|
||||
#1152908 by greg.1.anderson, msonnabaum, smk-ka, rbayliss: Fixed Remove calls to drush_backend_invoke()
|
||||
#813760 by galooph, tim.plunkett, Volx, raphaelhuefner | greg.harvey: Fixed CTools Page manager pages do not rever
|
||||
#904558 by hefox, voxpelli, Raines37 | loze: Fixed Multiple features and strongarm conflicts.
|
||||
#1170846 by Ravi.J: Fixed Disabling a feature does not disable dependent modules.
|
||||
#1313744 by TravisCarden: Fixed Sort 'Edit components' SELECT alphabetically.
|
||||
Made the Features Test not hidden, b/c if it is hidden it wont enable/install the content type
|
||||
#1300780 by Dave Reid: Added Provide an actual hook_features_pipe_alter() for general altering.
|
||||
#1178884 by YaderV | altfuns: Fixed Wrong word (with) in a description text.
|
||||
#893360 by Xen, Raines37, Grayside, franz: Added Drush update/recreate (add components).
|
||||
#913890 by Grayside, joshuajabbour, tim.plunkett | webchick: Added Generate .info file properties in order consist
|
||||
#1288028 by DamienMcKenna: Fixed D7 branch refers to D6 CCK hook.
|
||||
#1187232 by Ravi.J, rickvug: Fixed Hidden and disabled features should not be displayed in UI and should not be co
|
||||
|
||||
features DRUPAL-7--1-0-BETA4
|
||||
----------------------------
|
||||
|
||||
Fixing naming conflict b/w hook_enable and hook_disable
|
||||
#1187858 by smk-ka, febbraro: Fixed Node permissions fail to import.
|
||||
#1191558 by clemens.tolboom, Raines37: Allow for drush --destination while exporting a feature.
|
||||
#1188066 by smk-ka, febbraro: Fixed Excessive rebuilding if installing more than one feature at a time.
|
||||
#1260040 by tim.plunkett | hefox: Fixed features.css does not follow coding standards.
|
||||
#1195432: Fixing admin screen regression
|
||||
#1195432 by smk-ka: Use dependency information when importing
|
||||
#1186874 by dixon_: Better support for switching field storage types
|
||||
#1175684 by sagannotcarl, rocket.nova: Add legend/help to features-diff
|
||||
#1210604 by catch: drush features-revert-all does not respect --force argument
|
||||
#954062 by irakli, hefox: Incorrect Component Labels in the Components Dropdown
|
||||
#1186694 by tunic, fearlsgroove: Features orders exportes recursivally; fields allowed_values array alphabetized,
|
||||
#935152 by adamdicarlo, hadsie, hefox: Menu Items missing from Menu Links due to access callback user_is_anonymous
|
||||
#1258072 by hefox: Add suggestion for vocabulary when exporting a taxonomy field
|
||||
#1279212: add Features Breadcrumb
|
||||
Fixed the display of conflicting features. It was borked big time.
|
||||
#1219932 bu acrollet, t-dub, Nico Heulsen: Problems when dependencies have version numbers in them
|
||||
#1055460 by nedjo, hefox, fabsor, others...: Disabling a feature does not allow user to delete content type
|
||||
#994602 by franz: Misleading example on hook_features_export_alter() documentation
|
||||
#1264462 by Dave Reid, skwashd: template_preprocess_features_admin_components() calls D6-style theme() arguments
|
||||
#1131062: scripts/stylesheets manually added to info file have the path broken
|
||||
#946068 by rvilar, hefox: The machine name isn't updating correctly
|
||||
#1079440 by mrf, DamienMcKenna, dereine: Allow export of Custom Content Panes
|
||||
#1231118 by arnested, tim.plunkett: Coder fixes
|
||||
|
||||
features DRUPAL-7--1-0-BETA3
|
||||
----------------------------
|
||||
|
||||
#1177582 by paulsheldrake: Missing ctools plugin include
|
||||
#1191670: Undefined FEATURES_COMPONENT_NOT_FOUND in features.drush.inc
|
||||
#1157048 by dixon_: Features appear overridden
|
||||
#1124422 by anantagati: Wrong links in README
|
||||
#932104 by aaronbauman, hefox, etc.: Improve help for feature export drush command
|
||||
#1094940 by webflo: Features unit test are broken
|
||||
#1134202: Overridden views not reverting
|
||||
#1097560 by nedjo: Features incompatible with latest version of views
|
||||
|
||||
features DRUPAL-7--1-0-BETA2
|
||||
----------------------------
|
||||
|
||||
#1111714 by paranojik: _features_restore() does not properly revert components.
|
||||
#1070912 by bdragon: features_get_info() hands back original objects, can get corrupted.
|
||||
#1073988 by mikewink: File formats should be labeled text formats for consistency.
|
||||
removing unnecessary and typo-including check on plugin_api_hook_name
|
||||
removed duplicate ctools_plugin_api_get_hook function since new ctools is now released and it's unnecessary
|
||||
#1097560 by floretan, quartsize, merlinofchaos: Features incompatible with the latest version of views.
|
||||
Fixing unit test for permissions that broke as a result of #1063204
|
||||
Fixed the sanitize as it was incorrectly misidentifying a one item associative array as non-associative.
|
||||
Merge branch '7.x-1.x' of git.drupal.org:project/features into 7.x-1.x
|
||||
#1063204 by mrfree, careernerd, hadsie, irakli: Adding a new user role causes integrity constraing violation.
|
||||
Fixed E_STRICT warnings
|
||||
Merge branch '7.x-1.x' of git.drupal.org:project/features into 7.x-1.x
|
||||
#973836 by fmitchell, Grayside: Add @file to the generated files
|
||||
#1078972 by bdragon: Quoting problems on key side.
|
||||
Removing translation directories
|
||||
Stripping CVS keywords
|
||||
|
||||
features DRUPAL-7--1-0-BETA1
|
||||
----------------------------
|
||||
|
||||
#993314 by das-peter: Notice 'undefined index' in features_export_build_form_populate.
|
||||
adding warning @TODO to note an edge case that is not covered by existing code when checking if .module includes feature
|
||||
#1053336 by tema: Translatables are not exported for fields.
|
||||
#970788 by mori, das_peter, steinmb: Notice: Undefined index: api in _ctools_features_get_info() // error from featu
|
||||
#1062526 by fago, dasjo: components appear always overriden if the default hook makes use of hook-group.
|
||||
#954536 by klausi: Unsetting ids on vocabulary object breaks Entity Cache.
|
||||
#1014066 by brenk28: Improper use of t().
|
||||
#994122 by hadsie: Feature conflicts report displays conflicting features as Array instead of proper feature name.
|
||||
#953236 by isolesen, grobot : Make the Manage Features page more descriptive when there are no features.
|
||||
cleaning up previous somewhat extraneous commit and making first attempt towards fixing: #986118. Full fix of that one s
|
||||
#944584 by Rok Žlender, pcambra: Features diff in drush is broken.
|
||||
#1056422 by EclipseGc: Attribute Classes are Arrays in D7.
|
||||
Follow up to previous commit, actually ordering array correctly.
|
||||
Fix tab rendering on admin/build/features.
|
||||
Updated the filter test
|
||||
Utilize filter format machine names which core now provides.
|
||||
|
||||
features DRUPAL-7--1-0-ALPHA3
|
||||
-----------------------------
|
||||
|
||||
Update filter component to make use of machine names if available.
|
||||
PHP declaration.
|
||||
Update features_test field export to match changes in core.
|
||||
Several test fixes.
|
||||
Use features_get_default() rather than module_invoke().
|
||||
Remove old schema check.
|
||||
#925628 by thill_: Fix example version strings.
|
||||
#925332 by joshuajabbour: Cleaner export render for ctools components.
|
||||
|
||||
features DRUPAL-7--1-0-ALPHA2
|
||||
-----------------------------
|
||||
|
||||
Update features.api.php.
|
||||
#919500 by fago: Enable generic feature implementations. Introduces 'base' key in hook_features_api().
|
||||
#912132 by das-peter, fago: Implement hook_hook_info() entry for 'features_api'.
|
||||
Properly support 'hidden' key in D7.
|
||||
Fix function signature of features_export_prepare().
|
||||
Fix for menu links export options.
|
||||
Exclude bundles info from field config.
|
||||
Exclude 'module' key from image style export.
|
||||
Prevent features from going haywire when attempting to detect conflict with components it doesn't know about.
|
||||
Sorting field definitions before exporting.
|
||||
Removing non-class files from files[] in fino.
|
||||
Revert addition of files array to features.
|
||||
#912018 by das-peter: Fix for duplicate files[] entries.
|
||||
Remove unneeded context features hooks.
|
||||
Fix features_get_orphans().
|
||||
#908390: Generate files[] array for features.
|
||||
|
||||
features DRUPAL-7--1-0-ALPHA1
|
||||
-----------------------------
|
||||
|
||||
D7: Update image styles component.
|
||||
D7: Update hook_permission().
|
||||
D7: Remove _features_get_roles().
|
||||
D7: user_role and user_permission component upgrades.
|
||||
D7: Update list of component includes.
|
||||
D7: Filter format API updates.
|
||||
D7: Fix for diff integration.
|
||||
D7 fixes for menu_custom and menu_links components.
|
339
sites/all/modules/contrib/admin/features/LICENSE.txt
Normal file
339
sites/all/modules/contrib/admin/features/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.
|
236
sites/all/modules/contrib/admin/features/README.txt
Normal file
236
sites/all/modules/contrib/admin/features/README.txt
Normal file
@@ -0,0 +1,236 @@
|
||||
|
||||
Current state of Features for Drupal 7
|
||||
--------------------------------------
|
||||
Work on Features for D7 is currently aimed at getting to a point where Features
|
||||
can be used on a new install of Drupal 7 with features that were created on D7.
|
||||
Once this has been achieved, we will begin working on supporting D6 features as
|
||||
well as possibly supporting upgrades & migrations between legacy components and
|
||||
new equivalents (e.g. CCK to fields, imagecache to core image styles).
|
||||
|
||||
### Working components
|
||||
|
||||
- ctools
|
||||
- dependencies
|
||||
- field
|
||||
- filter
|
||||
- image
|
||||
- menu_custom
|
||||
- menu_links
|
||||
- node
|
||||
- taxonomy
|
||||
- user_permission
|
||||
- user_role
|
||||
- views
|
||||
|
||||
### Has changes to export format between D6 and D7
|
||||
|
||||
(@TODO legacy export compatibility)
|
||||
|
||||
- filter
|
||||
- taxonomy
|
||||
|
||||
### Requires upgrade/migration path
|
||||
|
||||
- imagecache > image
|
||||
- content > field
|
||||
|
||||
Note on the "Generate Feature" capability
|
||||
-----------------------------------------
|
||||
Features 7.x-2.x includes the ability to "Generate a feature" which saves it
|
||||
to the server disk. This can be a time-saving task in development. It requires
|
||||
the webserver to be able to write to the very code running the site and is
|
||||
not recommended for any environment other than a firewalled-off, local
|
||||
development environment (e.g. a person working alone on their laptop).
|
||||
|
||||
Features 1.x for Drupal 7.x
|
||||
---------------------------
|
||||
The features module enables the capture and management of features in Drupal. A
|
||||
feature is a collection of Drupal entities which taken together satisfy a
|
||||
certain use-case.
|
||||
|
||||
Features provides a UI and API for taking different site building components
|
||||
from modules with exportables and bundling them together in a single feature
|
||||
module. A feature module is like any other Drupal module except that it declares
|
||||
its components (e.g. views, contexts, CCK fields, etc.) in its `.info` file so
|
||||
that it can be checked, updated, or reverted programmatically.
|
||||
|
||||
Examples of features might be:
|
||||
|
||||
- A blog
|
||||
- A pressroom
|
||||
- An image gallery
|
||||
- An e-commerce t-shirt store
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
Features can be installed like any other Drupal module -- place it in the
|
||||
modules directory for your site and enable it on the `admin/build/modules` page.
|
||||
To take full advantage of some of the workflow benefits provided by Features,
|
||||
you should install [Drush][1].
|
||||
|
||||
If you plan on creating or working with very large features (greater than 1000
|
||||
items), you may need to increase PHP's max_input_vars configuration directive.
|
||||
For example, adding the following line to your .htaccess file will increase the
|
||||
max_input_vars directive to 3000:
|
||||
|
||||
php_value max_input_vars 3000
|
||||
|
||||
If you are using Suhosin, increasing suhosin.get.max_vars,
|
||||
suhosin.post.max_vars, and suhosin.request.max_vars may also be necessary.
|
||||
|
||||
|
||||
Basic usage
|
||||
-----------
|
||||
Features is geared toward usage by developers and site builders. It
|
||||
is not intended to be used by the general audience of your Drupal site.
|
||||
Features provides tools for accomplishing two important tasks:
|
||||
|
||||
### Task 1: Export features
|
||||
|
||||
You can build features in Drupal by using site building tools that are supported
|
||||
(see a short list under the *Compatibility* section).
|
||||
|
||||
Once you've built and configured functionality on a site, you can export it into
|
||||
a feature module by using the feature create page at
|
||||
`admin/structure/features/create`.
|
||||
|
||||
|
||||
### Task 2: Manage features
|
||||
|
||||
The features module also provides a way to manage features through a more
|
||||
targeted interface than `admin/modules`. The interface at
|
||||
`admin/structure/features` shows you only feature modules, and will also inform you
|
||||
if any of their components have been overridden. If this is the case, you can
|
||||
also re-create features to bring the module code up to date with any changes
|
||||
that have occurred in the database.
|
||||
|
||||
|
||||
Including custom code and adding to your feature
|
||||
------------------------------------------------
|
||||
Once you've exported your feature you will see that you have several files:
|
||||
|
||||
myfeature.info
|
||||
myfeature.module
|
||||
myfeature.[*].inc
|
||||
|
||||
You can add custom code (e.g. custom hook implementations, other functionality,
|
||||
etc.) to your feature in `myfeature.module` as you would with any other module.
|
||||
Do not change or add to any of the features `.inc` files unless you know what
|
||||
you are doing. These files are written to by features on updates so any custom
|
||||
changes may be overwritten.
|
||||
|
||||
|
||||
Using Features to manage development
|
||||
------------------------------------
|
||||
Because Features provides a centralized way to manage exportable components and
|
||||
write them to code it can be used during development in conjunction with a
|
||||
version control like SVN or git as a way to manage changes between development,
|
||||
staging and production sites. An example workflow for a developer using Features
|
||||
is to:
|
||||
|
||||
1. Make configuration changes to a feature on her local development site.
|
||||
2. Update her local feature codebase using `drush features-update`.
|
||||
3. Commit those changes using `svn commit`.
|
||||
4. Roll out her changes to the development site codebase by running `svn update`
|
||||
on the server. Other collaborating developers can also get her changes with
|
||||
`svn update`.
|
||||
5. Reverting any configuration on the staging site to match the updated codebase
|
||||
by running `drush features-revert`.
|
||||
6. Rinse, repeat.
|
||||
|
||||
Features also provides integration with the [Diff][3] module if enabled to show
|
||||
differences between configuration in the database and that in code. For site
|
||||
builders interested in using Features for development, enabling the diff module
|
||||
and reading `API.txt` for more details on the inner workings of Features is
|
||||
highly recommended.
|
||||
|
||||
|
||||
Drush usage
|
||||
-----------
|
||||
(requires Drush v4.5 or higher)
|
||||
|
||||
Features provides several useful drush commands:
|
||||
|
||||
- `drush features`
|
||||
|
||||
List all the available features on your site and their status.
|
||||
|
||||
- `drush features-export [feature name] [component list]`
|
||||
|
||||
Write a new feature in code containing the components listed.
|
||||
If called with no arguments, display a list of available components.
|
||||
If called with one argument, take the argument as a component name and
|
||||
attempt to create a feature with the same name.
|
||||
|
||||
The option '--destination=foo' may be used to specify the path (from Drupal
|
||||
root) where the feature should be created. The default destination is
|
||||
'sites/all/modules', though this can be overridden via the Features
|
||||
settings page.
|
||||
|
||||
- `drush features-update [feature name]`
|
||||
|
||||
Update the code of an existing feature to include any overrides/changes in
|
||||
your database (e.g. a new view).
|
||||
|
||||
- `drush features-revert [feature name]`
|
||||
|
||||
Revert the components of a feature in your site's database to the state
|
||||
described in your feature module's defaults.
|
||||
|
||||
- `drush features-diff [feature name]`
|
||||
|
||||
Show a diff between a feature's database components and those in code.
|
||||
Requires the Diff module.
|
||||
|
||||
Additional commands and options can be found using `drush help`.
|
||||
|
||||
|
||||
Compatibility
|
||||
-------------
|
||||
Features provides integration for the following exportables:
|
||||
|
||||
- CTools export API implementers (Context, Spaces, Boxes, Strongarm, Page
|
||||
Manager)
|
||||
- ImageCache
|
||||
- Views
|
||||
- [Other contributed modules][2]
|
||||
|
||||
Features also provides faux-exportable functionality for the following Drupal
|
||||
core and contrib components:
|
||||
|
||||
- Fields
|
||||
- Content types
|
||||
- Input filters
|
||||
- User roles/permissions
|
||||
- Custom menus and menu links *
|
||||
- Taxonomy vocabularies
|
||||
|
||||
* Currently in development.
|
||||
|
||||
|
||||
Security Concerns
|
||||
-----------------
|
||||
If you are using Features to export Roles and also use those Roles in other
|
||||
exportable code (like Views filters) you can wind up with an unintended
|
||||
security hole. When you import your Feature, if the Roles do not get created
|
||||
with the exact same Role IDs then your Views filters (or other component) will
|
||||
be referencing a different Role than you intended.
|
||||
|
||||
|
||||
For developers
|
||||
--------------
|
||||
Please read `API.txt` for more information about the concepts and integration
|
||||
points in the Features module.
|
||||
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
- febbraro (Frank Febbraro)
|
||||
- hefox (Fox)
|
||||
- mpotter (Mike Potter)
|
||||
- timplunkett (Tim Plunkett)
|
||||
|
||||
|
||||
[1]: http://drupal.org/project/drush
|
||||
[2]: (http://drupal.org/taxonomy/term/11478)
|
1497
sites/all/modules/contrib/admin/features/features.admin.inc
Normal file
1497
sites/all/modules/contrib/admin/features/features.admin.inc
Normal file
File diff suppressed because it is too large
Load Diff
476
sites/all/modules/contrib/admin/features/features.api.php
Normal file
476
sites/all/modules/contrib/admin/features/features.api.php
Normal file
@@ -0,0 +1,476 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Main info hook that features uses to determine what components are provided
|
||||
* by the implementing module.
|
||||
*
|
||||
* @return array
|
||||
* An array of components, keyed by the component name. Each component can
|
||||
* define several keys:
|
||||
*
|
||||
* 'file': Optional path to a file to include which contains the rest
|
||||
* of the features API hooks for this module.
|
||||
*
|
||||
* 'default_hook': The defaults hook for your component that is called
|
||||
* when the cache of default components is generated. Examples include
|
||||
* hook_views_default_views() or hook_context_default_contexts().
|
||||
*
|
||||
* 'default_file': The file-writing behavior to use when exporting this
|
||||
* component. May be one of 3 constant values:
|
||||
*
|
||||
* FEATURES_DEFAULTS_INCLUDED_COMMON: write hooks/components to
|
||||
* `.features.inc` with other components. This is the default behavior
|
||||
* if this key is not defined.
|
||||
*
|
||||
* FEATURES_DEFAULTS_INCLUDED: write hooks/components to a component-
|
||||
* specific include named automatically by features.
|
||||
*
|
||||
* FEATURES_DEFAULTS_CUSTOM: write hooks/components to a component-
|
||||
* specific include with a custom name provided. If your module provides
|
||||
* large amounts of code that should not be parsed often (only on specific
|
||||
* cache clears/rebuilds, for example) you should use the 2nd or 3rd
|
||||
* options to split your component into its own include.
|
||||
*
|
||||
* 'default_filename': The filename to use when 'default_file' is set to
|
||||
* FEATURES_DEFAULTS_CUSTOM.
|
||||
*
|
||||
* 'feature_source': Boolean value for whether this component should be
|
||||
* offered as an option on the initial feature creation form.
|
||||
*
|
||||
* 'base': Optional. An alternative base key to use when calling features
|
||||
* hooks for this component. Can be used for features component types that
|
||||
* are declared "dynamically" or are part of a family of components.
|
||||
*
|
||||
* 'alter_type': What type of alter hook this hook uses. 'normal' is called
|
||||
* after the main hook is called. 'inline' is embeded within the default hook
|
||||
* and may not be implemented by some default hooks.
|
||||
* 'none' is no alter hook exists. Defaults to 'normal'
|
||||
*
|
||||
* 'alter_hook': What the name of the alter hook for this component is.
|
||||
* Do not include the '_alter' part. Defaults to 'default_hook'.
|
||||
*/
|
||||
function hook_features_api() {
|
||||
return array(
|
||||
'mycomponent' => array(
|
||||
'default_hook' => 'mycomponent_defaults',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => TRUE,
|
||||
'file' => drupal_get_path('module', 'mycomponent') . '/mycomponent.features.inc',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component hook. The hook should be implemented using the name of the
|
||||
* component, not the module, eg. [component]_features_export() rather than
|
||||
* [module]_features_export().
|
||||
*
|
||||
* Process the export array for a given component. Implementations of this hook
|
||||
* have three key tasks:
|
||||
*
|
||||
* 1. Determine module dependencies for any of the components passed to it
|
||||
* e.g. the views implementation iterates over each views' handlers and
|
||||
* plugins to determine which modules need to be added as dependencies.
|
||||
*
|
||||
* 2. Correctly add components to the export array. In general this is usually
|
||||
* adding all of the items in $data to $export['features']['my_key'], but
|
||||
* can become more complicated if components are shared between features
|
||||
* or modules.
|
||||
*
|
||||
* 3. Delegating further detection and export tasks to related or derivative
|
||||
* components.
|
||||
*
|
||||
* Each export processor can kickoff further export processors by returning a
|
||||
* keyed array (aka the "pipe") where the key is the next export processor hook
|
||||
* to call and the value is an array to be passed to that processor's $data
|
||||
* argument. This allows an export process to start simply at a few objects:
|
||||
*
|
||||
* [context]
|
||||
*
|
||||
* And then branch out, delegating each component to its appropriate hook:
|
||||
*
|
||||
* [context]--------+------------+
|
||||
* | | |
|
||||
* [node] [block] [views]
|
||||
* |
|
||||
* [CCK]
|
||||
* |
|
||||
* [imagecache]
|
||||
*
|
||||
* @param array $data
|
||||
* An array of machine names for the component in question to be exported.
|
||||
* @param array &$export
|
||||
* By reference. An array of all components to be exported with a given
|
||||
* feature. Component objects that should be exported should be added to
|
||||
* this array.
|
||||
* @param string $module_name
|
||||
* The name of the feature module to be generated.
|
||||
* @return array
|
||||
* The pipe array of further processors that should be called.
|
||||
*/
|
||||
function hook_features_export($data, &$export, $module_name) {
|
||||
// The following is the simplest implementation of a straight object export
|
||||
// with no further export processors called.
|
||||
foreach ($data as $component) {
|
||||
$export['features']['mycomponent'][$component] = $component;
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Component hook. The hook should be implemented using the name of the
|
||||
* component, not the module, eg. [component]_features_export() rather than
|
||||
* [module]_features_export().
|
||||
*
|
||||
* List all objects for a component that may be exported.
|
||||
*
|
||||
* @return array
|
||||
* A keyed array of items, suitable for use with a FormAPI select or
|
||||
* checkboxes element.
|
||||
*/
|
||||
function hook_features_export_options() {
|
||||
$options = array();
|
||||
foreach (mycomponent_load() as $mycomponent) {
|
||||
$options[$mycomponent->name] = $mycomponent->title;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component hook. The hook should be implemented using the name of the
|
||||
* component, not the module, eg. [component]_features_export() rather than
|
||||
* [module]_features_export().
|
||||
*
|
||||
* Render one or more component objects to code.
|
||||
*
|
||||
* @param string $module_name
|
||||
* The name of the feature module to be exported.
|
||||
* @param array $data
|
||||
* An array of machine name identifiers for the objects to be rendered.
|
||||
* @param array $export
|
||||
* The full export array of the current feature being exported. This is only
|
||||
* passed when hook_features_export_render() is invoked for an actual feature
|
||||
* update or recreate, not during state checks or other operations.
|
||||
* @return array
|
||||
* An associative array of rendered PHP code where the key is the name of the
|
||||
* hook that should wrap the PHP code. The hook should not include the name
|
||||
* of the module, e.g. the key for `hook_example` should simply be `example`
|
||||
* The values in the array can also be in the form of an associative array
|
||||
* with the required key of 'code' and optional key of 'args', if 'args' need
|
||||
* to be added to the hook.
|
||||
*/
|
||||
function hook_features_export_render($module_name, $data, $export = NULL) {
|
||||
$code = array();
|
||||
$code[] = '$mycomponents = array();';
|
||||
foreach ($data as $name) {
|
||||
$code[] = " \$mycomponents['{$name}'] = " . features_var_export(mycomponent_load($name)) .";";
|
||||
}
|
||||
$code[] = "return \$mycomponents;";
|
||||
$code = implode("\n", $code);
|
||||
return array('mycomponent_defaults' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component hook. The hook should be implemented using the name of the
|
||||
* component, not the module, eg. [component]_features_export() rather than
|
||||
* [module]_features_export().
|
||||
*
|
||||
* Revert all component objects for a given feature module.
|
||||
*
|
||||
* @param string $module_name
|
||||
* The name of the feature module whose components should be reverted.
|
||||
* @return boolean
|
||||
* TRUE or FALSE for whether the components were successfully reverted.
|
||||
* NOTE: This return value is no longer used in the latest Features so
|
||||
* modules should no longer count on this value
|
||||
*/
|
||||
function hook_features_revert($module_name) {
|
||||
$mycomponents = module_invoke($module_name, 'mycomponent_defaults');
|
||||
if (!empty($mycomponents)) {
|
||||
foreach ($mycomponents as $mycomponent) {
|
||||
mycomponent_delete($mycomponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component hook. The hook should be implemented using the name of the
|
||||
* component, not the module, eg. [component]_features_export() rather than
|
||||
* [module]_features_export().
|
||||
*
|
||||
* Rebuild all component objects for a given feature module. Should only be
|
||||
* implemented for 'faux-exportable' components.
|
||||
*
|
||||
* This hook is called at points where Features determines that it is safe
|
||||
* (ie. the feature is in state `FEATURES_REBUILDABLE`) for your module to
|
||||
* replace objects in the database with defaults that you collect from your
|
||||
* own defaults hook. See API.txt for how Features determines whether a
|
||||
* rebuild of components is possible.
|
||||
*
|
||||
* @param string $module_name
|
||||
* The name of the feature module whose components should be rebuilt.
|
||||
*/
|
||||
function hook_features_rebuild($module_name) {
|
||||
$mycomponents = module_invoke($module_name, 'mycomponent_defaults');
|
||||
if (!empty($mycomponents)) {
|
||||
foreach ($mycomponents as $mycomponent) {
|
||||
mycomponent_save($mycomponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the final array of Component names to be exported, just prior to
|
||||
* the rendering of defaults. Allows modules a final say in whether or not
|
||||
* certain Components are exported (the Components' actual data, however,
|
||||
* cannot be altered by this hook).
|
||||
*
|
||||
* @param array &$export
|
||||
* By reference. An array of all component names to be exported with a given
|
||||
* feature.
|
||||
* @param array $module_name
|
||||
* The name of the feature module to be generated.
|
||||
*/
|
||||
function hook_features_export_alter(&$export, $module_name) {
|
||||
// Example: do not allow the page content type to be exported, ever.
|
||||
if (!empty($export['features']['node']['page'])) {
|
||||
unset($export['features']['node']['page']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the pipe array for a given component. This hook should be implemented
|
||||
* with the name of the component type in place of `component` in the function
|
||||
* name, e.g. `features_pipe_views_alter()` will alter the pipe for the Views
|
||||
* component.
|
||||
*
|
||||
* @param array &$pipe
|
||||
* By reference. The pipe array of further processors that should be called.
|
||||
* @param array $data
|
||||
* An array of machine names for the component in question to be exported.
|
||||
* @param array &$export
|
||||
* By reference. An array of all components to be exported with a given
|
||||
* feature.
|
||||
*/
|
||||
function hook_features_pipe_COMPONENT_alter(&$pipe, $data, $export) {
|
||||
if (in_array($data, 'my-node-type')) {
|
||||
$pipe['dependencies'][] = 'mymodule';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the pipe array for a given component.
|
||||
*
|
||||
* @param array &$pipe
|
||||
* By reference. The pipe array of further processors that should be called.
|
||||
* @param array $data
|
||||
* An array of machine names for the component in question to be exported.
|
||||
* @param array &$export
|
||||
* By reference. An array of all components to be exported with a given
|
||||
* feature.
|
||||
*
|
||||
* The component being exported is contained in $export['component'].
|
||||
* The module being exported contained in $export['module_name'].
|
||||
*/
|
||||
function hook_features_pipe_alter(&$pipe, $data, $export) {
|
||||
if ($export['component'] == 'node' && in_array($data, 'my-node-type')) {
|
||||
$pipe['dependencies'][] = 'mymodule';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup features_component_alter_hooks Feature's component alter hooks
|
||||
* @{
|
||||
* Hooks to modify components defined by other features. These come in the form
|
||||
* hook_COMPONENT_alter where COMPONENT is the default_hook declared by any of
|
||||
* components within features.
|
||||
*
|
||||
* CTools also has a variety of hook_FOO_alters.
|
||||
*
|
||||
* Note: While views is a component of features, it declares it's own alter
|
||||
* function which takes a similar form:
|
||||
* hook_views_default_views_alter(&$views)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter the default fields right before they are cached into the database.
|
||||
*
|
||||
* @param &$fields
|
||||
* By reference. The fields that have been declared by another feature.
|
||||
*/
|
||||
function hook_field_default_fields_alter(&$fields) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the default fieldgroup groups right before they are cached into the
|
||||
* database.
|
||||
*
|
||||
* @param &$groups
|
||||
* By reference. The fieldgroup groups that have been declared by another
|
||||
* feature.
|
||||
*/
|
||||
function hook_fieldgroup_default_groups_alter(&$groups) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the default filter formats right before they are cached into the
|
||||
* database.
|
||||
*
|
||||
* @param &$formats
|
||||
* By reference. The formats that have been declared by another feature.
|
||||
*/
|
||||
function hook_filter_default_formats_alter(&$formats) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the default menus right before they are cached into the database.
|
||||
*
|
||||
* @param &$menus
|
||||
* By reference. The menus that have been declared by another feature.
|
||||
*/
|
||||
function hook_menu_default_menu_custom_alter(&$menus) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the default menu links right before they are cached into the database.
|
||||
*
|
||||
* @param &$links
|
||||
* By reference. The menu links that have been declared by another feature.
|
||||
*/
|
||||
function hook_menu_default_menu_links_alter(&$links) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the default menu items right before they are cached into the database.
|
||||
*
|
||||
* @param &$items
|
||||
* By reference. The menu items that have been declared by another feature.
|
||||
*/
|
||||
function hook_menu_default_items_alter(&$items) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the default vocabularies right before they are cached into the
|
||||
* database.
|
||||
*
|
||||
* @param &$vocabularies
|
||||
* By reference. The vocabularies that have been declared by another feature.
|
||||
*/
|
||||
function hook_taxonomy_default_vocabularies_alter(&$vocabularies) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the default permissions right before they are cached into the
|
||||
* database.
|
||||
*
|
||||
* @param &$permissions
|
||||
* By reference. The permissions that have been declared by another feature.
|
||||
*/
|
||||
function hook_user_default_permissions_alter(&$permissions) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the default roles right before they are cached into the database.
|
||||
*
|
||||
* @param &$roles
|
||||
* By reference. The roles that have been declared by another feature.
|
||||
*/
|
||||
function hook_user_default_roles_alter(&$roles) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup features_module_hooks Feature module hooks
|
||||
* @{
|
||||
* Hooks invoked on Feature modules when that module is enabled, disabled,
|
||||
* rebuilt, or reverted. These are ONLY invoked on the Features module on
|
||||
* which these actions are taken.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Feature module hook. Invoked on a Feature module before that module is
|
||||
* reverted.
|
||||
*
|
||||
* @param $component
|
||||
* String name of the component that is about to be reverted.
|
||||
*/
|
||||
function hook_pre_features_revert($component) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature module hook. Invoked on a Feature module after that module is
|
||||
* reverted.
|
||||
*
|
||||
* @param $component
|
||||
* String name of the component that has just been reverted.
|
||||
*/
|
||||
function hook_post_features_revert($component) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature module hook. Invoked on a Feature module before that module is
|
||||
* rebuilt.
|
||||
*
|
||||
* @param $component
|
||||
* String name of the component that is about to be rebuilt.
|
||||
*/
|
||||
function hook_pre_features_rebuild($component) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature module hook. Invoked on a Feature module after that module is
|
||||
* rebuilt.
|
||||
*
|
||||
* @param $component
|
||||
* String name of the component that has just been rebuilt.
|
||||
*/
|
||||
function hook_post_features_rebuild($component) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature module hook. Invoked on a Feature module before that module is
|
||||
* disabled.
|
||||
*
|
||||
* @param $component
|
||||
* String name of the component that is about to be disabled.
|
||||
*/
|
||||
function hook_pre_features_disable_feature($component) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature module hook. Invoked on a Feature module after that module is
|
||||
* disabled.
|
||||
*
|
||||
* @param $component
|
||||
* String name of the component that has just been disabled.
|
||||
*/
|
||||
function hook_post_features_disable_feature($component) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature module hook. Invoked on a Feature module before that module is
|
||||
* enabled.
|
||||
*
|
||||
* @param $component
|
||||
* String name of the component that is about to be enabled.
|
||||
*/
|
||||
function hook_pre_features_enable_feature($component) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature module hook. Invoked on a Feature module after that module is
|
||||
* enabled.
|
||||
*
|
||||
* @param $component
|
||||
* String name of the component that has just been enabled.
|
||||
*/
|
||||
function hook_post_features_enable_feature($component) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
566
sites/all/modules/contrib/admin/features/features.css
Normal file
566
sites/all/modules/contrib/admin/features/features.css
Normal file
@@ -0,0 +1,566 @@
|
||||
/**
|
||||
* Features packages.
|
||||
*/
|
||||
div.features-form-links {
|
||||
width:20%;
|
||||
float:left;
|
||||
}
|
||||
|
||||
div.features-form-content {
|
||||
width:80%;
|
||||
float:right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package links.
|
||||
*/
|
||||
div.features-form-links ul#features-form-links,
|
||||
div.features-form-links ul#features-form-links li,
|
||||
div.features-form-links ul#features-form-links li a {
|
||||
display:block;
|
||||
float:none;
|
||||
padding:0px;
|
||||
margin:0px;
|
||||
}
|
||||
|
||||
div.features-form-links ul#features-form-links {
|
||||
margin:0px 0px 10px;
|
||||
}
|
||||
|
||||
div.features-form-links ul#features-form-links li a {
|
||||
background:#f8f8f8;
|
||||
padding:5px 5px 4px 4px;
|
||||
border-left:1px solid #eee;
|
||||
border-bottom:1px solid #eee;
|
||||
color: #000;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
div.features-form-links ul#features-form-links li a.features-package-active {
|
||||
padding:4px 5px 4px 4px;
|
||||
background:#fff;
|
||||
border:1px solid #ccc;
|
||||
border-right:0px;
|
||||
color: #000;
|
||||
margin-right:-1px;
|
||||
}
|
||||
|
||||
/* Packages */
|
||||
div.features-form-package {
|
||||
border:1px solid #ccc;
|
||||
background:#fff;
|
||||
color: #000;
|
||||
padding:10px;
|
||||
margin:0px 0px 20px;
|
||||
display:none;
|
||||
}
|
||||
|
||||
div.features-package-active {
|
||||
display:block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Features management form (admin/build/features).
|
||||
*/
|
||||
div.features-empty {
|
||||
margin:15px 0px;
|
||||
font-size:1.5em;
|
||||
text-align:center;
|
||||
color:#999;
|
||||
}
|
||||
|
||||
form div.buttons {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
table.features .admin-loading,
|
||||
table.features tr.disabled {
|
||||
color:#999;
|
||||
}
|
||||
|
||||
table.features a.configure {
|
||||
float:right;
|
||||
font-style:italic;
|
||||
}
|
||||
table.features div.feature {
|
||||
float:left; width:85%;
|
||||
}
|
||||
table.features div.feature strong {
|
||||
font-size:13px;
|
||||
}
|
||||
table.features div.feature div.description {
|
||||
font-size:11px; margin:0px;
|
||||
}
|
||||
|
||||
table.features-manage td.name {
|
||||
width:80%;
|
||||
}
|
||||
table.features-manage td.sign {
|
||||
width:20%;
|
||||
}
|
||||
|
||||
table.features-admin td.name {
|
||||
width:60%;
|
||||
}
|
||||
table.features-admin td.sign {
|
||||
width:20%;
|
||||
}
|
||||
table.features-admin td.state {
|
||||
width:10%;
|
||||
}
|
||||
table.features-admin td.actions {
|
||||
width:10%;
|
||||
}
|
||||
|
||||
table.features td.sign {
|
||||
font-size:9px;
|
||||
line-height:15px;
|
||||
white-space:nowrap;
|
||||
}
|
||||
|
||||
table.features td.sign * {
|
||||
margin:0px;
|
||||
}
|
||||
|
||||
table.features .admin-check,
|
||||
table.features .admin-default,
|
||||
table.features .admin-overridden,
|
||||
table.features .admin-rebuilding,
|
||||
table.features .admin-needs-review {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature export form (admin/build/features/export).
|
||||
*/
|
||||
form.features-export-form table td {
|
||||
width:50%;
|
||||
}
|
||||
form.features-export-form table td {
|
||||
vertical-align:top;
|
||||
}
|
||||
form.features-export-form table div.description {
|
||||
white-space:normal;
|
||||
}
|
||||
|
||||
table.features-export div.form-item {
|
||||
white-space:normal;
|
||||
}
|
||||
table.features-export select {
|
||||
width:90%;
|
||||
}
|
||||
table.features-export td {
|
||||
vertical-align:top;
|
||||
}
|
||||
|
||||
form.features-export-form div.features-select {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/*
|
||||
form.features-export-form div.form-checkboxes {
|
||||
overflow-x:hidden;
|
||||
overflow-y:auto;
|
||||
height:20em;
|
||||
}
|
||||
*/
|
||||
|
||||
form.features-export-form div#edit-components-wrapper,
|
||||
form.features-export-form div.features-select {
|
||||
padding-right:20px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature component display (admin/build/features/%feature).
|
||||
*/
|
||||
div.features-components div.column {
|
||||
float:left;
|
||||
width:50%;
|
||||
}
|
||||
|
||||
div.features-components div.column div.info {
|
||||
padding-right:20px;
|
||||
}
|
||||
div.features-components div.column div.components {
|
||||
padding-left:20px;
|
||||
}
|
||||
|
||||
h3.features-download,
|
||||
div.features-comparison h3,
|
||||
div.features-components h3 {
|
||||
font-size:2em;
|
||||
font-weight:bold;
|
||||
letter-spacing:-1px;
|
||||
margin:15px 0px;
|
||||
}
|
||||
|
||||
h3.features-download {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
div.features-components div.description {
|
||||
font-size:11px;
|
||||
margin:15px 0px;
|
||||
}
|
||||
|
||||
div.features-components table td {
|
||||
font-size:11px;
|
||||
}
|
||||
div.features-components table td.component {
|
||||
padding-left:20px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Features component lists.
|
||||
*/
|
||||
span.features-component-key {
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
a.admin-update,
|
||||
a.features-storage,
|
||||
span.features-storage,
|
||||
span.features-component-list span {
|
||||
white-space:nowrap;
|
||||
margin-right:5px;
|
||||
padding:2px 5px;
|
||||
background:#eee;
|
||||
-moz-border-radius:5px;
|
||||
-webkit-border-radius:5px;
|
||||
}
|
||||
|
||||
div.features-key span.admin-conflict,
|
||||
span.features-component-list span.features-conflict {
|
||||
background-color: #c30 !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#features-export-wrapper .features-conflict,
|
||||
#features-export-wrapper .features-conflict label.option{
|
||||
color: #c30 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
div.conflicts span.admin-disabled {
|
||||
color: #955;
|
||||
}
|
||||
|
||||
a.admin-update {
|
||||
background:transparent;
|
||||
}
|
||||
|
||||
/* These pseudo selectors are necessary for themes like Garland. */
|
||||
a.admin-overridden:link,
|
||||
a.admin-overridden:visited,
|
||||
span.admin-overridden {
|
||||
color:#fff;
|
||||
background:#666;
|
||||
}
|
||||
|
||||
a.admin-needs-review:link,
|
||||
a.admin-needs-review:visited,
|
||||
span.admin-needs-review {
|
||||
color:#963;
|
||||
background:#fe6;
|
||||
}
|
||||
|
||||
a.admin-rebuilding:link,
|
||||
a.admin-rebuilding:visited,
|
||||
span.admin-rebuilding {
|
||||
color:#fff;
|
||||
background:#699;
|
||||
}
|
||||
|
||||
a.admin-conflict:link,
|
||||
a.admin-conflict:visited,
|
||||
span.admin-conflict {
|
||||
color:#c30;
|
||||
}
|
||||
|
||||
table.features-diff td.diff-addedline,
|
||||
span.features-component-list .features-detected {
|
||||
color:#68a;
|
||||
background:#def;
|
||||
}
|
||||
|
||||
table.features-diff td.diff-deletedline,
|
||||
span.features-component-list .features-dependency {
|
||||
color:#999;
|
||||
background:#f8f8f8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Features diff.
|
||||
*/
|
||||
table.features-diff {
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
table.features-diff td {
|
||||
padding:0px 5px;
|
||||
}
|
||||
|
||||
table.features-diff td.diff-deletedline,
|
||||
table.features-diff td.diff-addedline,
|
||||
table.features-diff td.diff-context {
|
||||
width:50%;
|
||||
font-family:'Andale Mono',monospace;
|
||||
}
|
||||
|
||||
/**
|
||||
* New UI component export list
|
||||
*/
|
||||
#features-export-wrapper div.features-export-parent {
|
||||
clear: both;
|
||||
}
|
||||
#features-export-form .fieldset-content.fieldset-wrapper {
|
||||
padding-top: 5px;
|
||||
}
|
||||
html.js #features-export-form fieldset.collapsed {
|
||||
min-height: 2em;
|
||||
margin-bottom: 0;
|
||||
top: 0;
|
||||
padding: 0 0 0 0;
|
||||
}
|
||||
fieldset.features-export-component {
|
||||
background: #F3F8FB;
|
||||
margin: 0.5em 0;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
top: 0;
|
||||
}
|
||||
fieldset.features-export-component.collapsed {
|
||||
background: #F3F8FB;
|
||||
}
|
||||
fieldset.features-export-component legend {
|
||||
top: 0;
|
||||
}
|
||||
fieldset.features-export-component.collapsed .fieldset-wrapper {
|
||||
margin: 0;
|
||||
padding: 0 13px 6px 15px;
|
||||
}
|
||||
fieldset.features-export-component .fieldset-legend {
|
||||
white-space: nowrap;
|
||||
}
|
||||
fieldset.features-export-component .fieldset-title span {
|
||||
font-size: 11px;
|
||||
text-transform: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
#features-export-wrapper .fieldset-wrapper {
|
||||
font-size: 12px;
|
||||
}
|
||||
div.features-export-list {
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
border: 1px solid #CCC;
|
||||
border-top-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#features-export-form .fieldset-description {
|
||||
margin: 5px 0;
|
||||
line-height: 1.231em;
|
||||
font-size: 0.923em;
|
||||
color: #666;
|
||||
}
|
||||
div.features-export-empty {
|
||||
display: none;
|
||||
}
|
||||
fieldset.features-export-component .fieldset-wrapper .form-checkboxes {
|
||||
max-height: 20em;
|
||||
overflow: auto;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
#features-export-wrapper .component-select .form-type-checkbox {
|
||||
overflow: hidden;
|
||||
padding: 0 0 0 2px;
|
||||
}
|
||||
|
||||
#features-export-wrapper .component-added .form-type-checkbox,
|
||||
#features-export-wrapper .component-detected .form-type-checkbox,
|
||||
#features-export-wrapper .component-included .form-type-checkbox {
|
||||
float: left;
|
||||
white-space: normal;
|
||||
margin: 2px 5px;
|
||||
padding: 0 5px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#features-export-wrapper .component-added .form-type-checkbox {
|
||||
font-weight: bold;
|
||||
background: #EEE;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
#features-export-wrapper div.component-added label.option {
|
||||
font-weight: bold;
|
||||
}
|
||||
#features-export-wrapper .component-detected .form-type-checkbox {
|
||||
font-style: italic;
|
||||
color:#68a;
|
||||
background:#def;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
|
||||
#features-export-info {
|
||||
width: 49%;
|
||||
float: left;
|
||||
position: relative;
|
||||
}
|
||||
#features-export-wrapper {
|
||||
width: 49%;
|
||||
float: right;
|
||||
clear: both;
|
||||
position: relative;
|
||||
}
|
||||
#features-export-advanced {
|
||||
width: 49%;
|
||||
float: left;
|
||||
clear: left;
|
||||
margin-top: 0.5em;
|
||||
position: relative;
|
||||
}
|
||||
#features-export-buttons {
|
||||
width: 49%;
|
||||
float: left;
|
||||
margin-top: 0.5em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
#features-export-buttons input {
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
#features-export-wrapper .component-added label a,
|
||||
#features-export-wrapper .component-detected label a,
|
||||
#features-export-wrapper .component-included label a {
|
||||
display: inline;
|
||||
float: none;
|
||||
}
|
||||
#features-export-wrapper label.component-added {
|
||||
font-weight: bold;
|
||||
}
|
||||
#features-export-form input[size="60"].form-text {
|
||||
width: 100%;
|
||||
}
|
||||
input.form-submit.features-refresh-button {
|
||||
font-size: 0.7em;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
background-color: transparent;
|
||||
margin-left: 1em;
|
||||
}
|
||||
.features-refresh-wrapper .ajax-progress {
|
||||
font-size: 10px;
|
||||
width: 10em;
|
||||
}
|
||||
#features-export-advanced .ajax-progress {
|
||||
font-size: 10px;
|
||||
width: 10em;
|
||||
}
|
||||
#features-export-wrapper div.fieldset-description,
|
||||
#features-export-wrapper div.description {
|
||||
clear: both;
|
||||
}
|
||||
#features-filter input[size="60"].form-text {
|
||||
width: 200px;
|
||||
}
|
||||
#features-filter .fieldset-content,
|
||||
#features-filter .fieldset-wrapper,
|
||||
#features-filter fieldset {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#features-filter fieldset legend {
|
||||
display: none;
|
||||
}
|
||||
#features-filter label,
|
||||
#features-filter input {
|
||||
display: inline;
|
||||
width: auto;
|
||||
}
|
||||
#features-filter .form-item {
|
||||
float: left;
|
||||
margin: 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
#features-filter .form-item.form-type-checkbox {
|
||||
margin: 5px 0;
|
||||
}
|
||||
#features-filter span {
|
||||
float: left;
|
||||
white-space: normal;
|
||||
margin: 5px 5px;
|
||||
padding: 0 5px;
|
||||
background: transparent;
|
||||
background: #EEE;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#features-filter span:hover {
|
||||
background:#def;
|
||||
}
|
||||
#features-autodetect .form-item {
|
||||
float: left;
|
||||
margin: 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
#features-autodetect .fieldset-content,
|
||||
#features-autodetect .fieldset-wrapper,
|
||||
#features-autodetect fieldset {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 1em 0 0 0;
|
||||
top: 0;
|
||||
}
|
||||
#features-autodetect fieldset legend {
|
||||
display: none;
|
||||
}
|
||||
#features-export-advanced .form-item.form-item-generate-path {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#features-info-file .form-textarea-wrapper,
|
||||
#features-info-file textarea {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#features-info-file .form-type-textarea {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
#edit-info-preview {
|
||||
margin: 1em 0;
|
||||
}
|
||||
#features-legend .fieldset-wrapper span {
|
||||
font-style: normal;
|
||||
color: black;
|
||||
display: inline-block;
|
||||
background: transparent;
|
||||
border: 1px solid #DDD;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
white-space: nowrap;
|
||||
padding: 0 8px;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
#features-legend .fieldset-wrapper .component-detected {
|
||||
font-style: italic;
|
||||
color:#68a;
|
||||
background:#def;
|
||||
border-width: 0;
|
||||
}
|
||||
#features-legend .fieldset-wrapper .component-added {
|
||||
font-weight: bold;
|
||||
background: #EEE;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
fieldset.features-export-component .fieldset-title .component-count {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
911
sites/all/modules/contrib/admin/features/features.drush.inc
Normal file
911
sites/all/modules/contrib/admin/features/features.drush.inc
Normal file
@@ -0,0 +1,911 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Features module drush integration.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_drush_command().
|
||||
*
|
||||
* @return
|
||||
* An associative array describing your command(s).
|
||||
*
|
||||
* @see drush_parse_command()
|
||||
*/
|
||||
function features_drush_command() {
|
||||
$items = array();
|
||||
|
||||
// If Features is enabled display the configured default export path,
|
||||
// otherwise just show the default.
|
||||
if (defined('FEATURES_DEFAULT_EXPORT_PATH')) {
|
||||
$path = variable_get('features_default_export_path', FEATURES_DEFAULT_EXPORT_PATH);
|
||||
}
|
||||
else {
|
||||
$path = 'sites/all/modules';
|
||||
}
|
||||
|
||||
$items['features-list'] = array(
|
||||
'description' => "List all the available features for your site.",
|
||||
'options' => array(
|
||||
'status' => "Feature status, can be 'enabled', 'disabled' or 'all'",
|
||||
),
|
||||
'drupal dependencies' => array('features'),
|
||||
'aliases' => array('fl', 'features'),
|
||||
);
|
||||
$items['features-export'] = array(
|
||||
'description' => "Export a feature from your site into a module.",
|
||||
'arguments' => array(
|
||||
'feature' => 'Feature name to export.',
|
||||
'components' => 'Patterns of components to include, see features-components for the format of patterns.'
|
||||
),
|
||||
'options' => array(
|
||||
'destination' => "Destination path (from Drupal root) of the exported feature. Defaults to '" . $path . "'.",
|
||||
'version-set' => "Specify a version number for the feature.",
|
||||
'version-increment' => "Increment the feature's version number.",
|
||||
),
|
||||
'drupal dependencies' => array('features'),
|
||||
'aliases' => array('fe'),
|
||||
);
|
||||
$items['features-add'] = array(
|
||||
'description' => "Add a component to a feature module. (DEPRECATED: use features-export)",
|
||||
'arguments' => array(
|
||||
'feature' => 'Feature name to add to.',
|
||||
'components' => 'List of components to add.',
|
||||
),
|
||||
'options' => array(
|
||||
'version-set' => "Specify a version number for the feature.",
|
||||
'version-increment' => "Increment the feature's version number.",
|
||||
),
|
||||
'drupal dependencies' => array('features'),
|
||||
'aliases' => array('fa'),
|
||||
);
|
||||
$items['features-components'] = array(
|
||||
'description' => 'List features components.',
|
||||
'arguments' => array(
|
||||
'patterns' => 'The features components type to list. Omit this argument to list all components.',
|
||||
),
|
||||
'options' => array(
|
||||
'exported' => array(
|
||||
'description' => 'Show only components that have been exported.',
|
||||
),
|
||||
'not-exported' => array(
|
||||
'description' => 'Show only components that have not been exported.',
|
||||
),
|
||||
),
|
||||
'aliases' => array('fc'),
|
||||
);
|
||||
$items['features-update'] = array(
|
||||
'description' => "Update a feature module on your site.",
|
||||
'arguments' => array(
|
||||
'feature' => 'A space delimited list of features.',
|
||||
),
|
||||
'options' => array(
|
||||
'version-set' => "Specify a version number for the feature.",
|
||||
'version-increment' => "Increment the feature's version number.",
|
||||
),
|
||||
'drupal dependencies' => array('features'),
|
||||
'aliases' => array('fu'),
|
||||
);
|
||||
$items['features-update-all'] = array(
|
||||
'description' => "Update all feature modules on your site.",
|
||||
'arguments' => array(
|
||||
'feature_exclude' => 'A space-delimited list of features to exclude from being updated.',
|
||||
),
|
||||
'drupal dependencies' => array('features'),
|
||||
'aliases' => array('fu-all', 'fua'),
|
||||
);
|
||||
$items['features-revert'] = array(
|
||||
'description' => "Revert a feature module on your site.",
|
||||
'arguments' => array(
|
||||
'feature' => 'A space delimited list of features or feature.component pairs.',
|
||||
),
|
||||
'options' => array(
|
||||
'force' => "Force revert even if Features assumes components' state are default.",
|
||||
),
|
||||
'examples' => array(
|
||||
'drush fr foo.node foo.taxonomy bar' => 'Revert node and taxonomy components of feature "foo", but only if they are overriden. Revert all overriden components of feature "bar".',
|
||||
'drush fr foo.node foo.taxonomy bar --force' => 'Revert node and taxonomy components of feature "foo". Revert all components of feature "bar".',
|
||||
),
|
||||
'drupal dependencies' => array('features'),
|
||||
'aliases' => array('fr'),
|
||||
);
|
||||
$items['features-revert-all'] = array(
|
||||
'description' => "Revert all enabled feature module on your site.",
|
||||
'arguments' => array(
|
||||
'feature_exclude' => 'A space-delimited list of features to exclude from being reverted.',
|
||||
),
|
||||
'options' => array(
|
||||
'force' => "Force revert even if Features assumes components' state are default.",
|
||||
),
|
||||
'drupal dependencies' => array('features'),
|
||||
'aliases' => array('fr-all', 'fra'),
|
||||
);
|
||||
$items['features-diff'] = array(
|
||||
'description' => "Show the difference between the default and overridden state of a feature.",
|
||||
'arguments' => array(
|
||||
'feature' => 'The feature in question.',
|
||||
),
|
||||
'options' => array(
|
||||
'ctypes' => 'Comma separated list of component types to limit the output to. Defaults to all types.',
|
||||
'lines' => 'Generate diffs with <n> lines of context instead of the usual two.',
|
||||
),
|
||||
'drupal dependencies' => array('features', 'diff'),
|
||||
'aliases' => array('fd'),
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_drush_help().
|
||||
*/
|
||||
function features_drush_help($section) {
|
||||
// If Features is enabled display the configured default export path,
|
||||
// otherwise just show the default.
|
||||
if (defined('FEATURES_DEFAULT_EXPORT_PATH')) {
|
||||
$path = variable_get('features_default_export_path', FEATURES_DEFAULT_EXPORT_PATH);
|
||||
}
|
||||
else {
|
||||
$path = 'sites/all/modules';
|
||||
}
|
||||
|
||||
switch ($section) {
|
||||
case 'drush:features':
|
||||
return dt("List all the available features for your site.");
|
||||
case 'drush:features-export':
|
||||
return dt("Export a feature from your site into a module. If called with no arguments, display a list of available components. If called with a single argument, attempt to create a feature including the given component with the same name. The option '--destination=foo' may be used to specify the path (from Drupal root) where the feature should be created. The default destination is '@path'. The option '--version-set=foo' may be used to specify a version number for the feature or the option '--version-increment' may also to increment the feature's version number.", array('@path' => $path));
|
||||
case 'drush:features-components':
|
||||
return dt("List feature components matching patterns. The listing may be limited to exported/not-exported components.
|
||||
|
||||
A component pattern consists of a source, a colon and a component. Both source and component may be a full name (as in \"dependencies\"), a shorthand (for instance \"dep\") or a pattern (like \"%denci%\").
|
||||
|
||||
Shorthands are unique shortenings of a name. They will only match if exactly one option contains the shorthand. So in a standard installation, \"dep\" will work for dependencies, but \"user\" wont, as it matches both user_permission and user_role.
|
||||
|
||||
Patterns uses * or % for matching multiple sources/components. Unlike shorthands, patterns must match the whole name, so \"field:%article%\" should be used to select all fields containing \"article\" (which could both be those on the node type article, as well as those fields named article). * and % are equivalent, but the latter doesn't have to be escaped in UNIX shells.
|
||||
|
||||
Lastly, a pattern without a colon is interpreted as having \":%\" appended, for easy listing of all components of a source.
|
||||
");
|
||||
case 'drush:features-update':
|
||||
return dt("Update a feature module on your site. The option '--version-set=foo' may be used to specify a version number for the feature or the option '--version-increment' may also to increment the feature's version number.");
|
||||
case 'drush:features-update-all':
|
||||
return dt("Update all feature modules on your site.");
|
||||
case 'drush:features-revert':
|
||||
return dt("Revert a feature module on your site.");
|
||||
case 'drush:features-revert-all':
|
||||
return dt("Revert all enabled feature module on your site.");
|
||||
case 'drush:features-diff':
|
||||
return dt("Show a diff of a feature module.");
|
||||
case 'drush:features-add':
|
||||
return dt("Add a component to a feature module. The option '--version-set=foo' may be used to specify a version number for the feature or the option '--version-increment' may also to increment the feature's version number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all feature modules.
|
||||
*/
|
||||
function drush_features_list() {
|
||||
$status = drush_get_option('status') ? drush_get_option('status') : 'all';
|
||||
if (!in_array($status, array('enabled', 'disabled', 'all'))) {
|
||||
return drush_set_error('', dt('!status is not valid', array('!status' => $status)));
|
||||
}
|
||||
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
$rows = array(array(dt('Name'), dt('Feature'), dt('Status'), dt('Version'), dt('State')));
|
||||
|
||||
// Sort the Features list before compiling the output.
|
||||
$features = features_get_features(NULL, TRUE);
|
||||
ksort($features);
|
||||
|
||||
foreach ($features as $k => $m) {
|
||||
switch (features_get_storage($m->name)) {
|
||||
case FEATURES_DEFAULT:
|
||||
case FEATURES_REBUILDABLE:
|
||||
$storage = '';
|
||||
break;
|
||||
case FEATURES_OVERRIDDEN:
|
||||
$storage = dt('Overridden');
|
||||
break;
|
||||
case FEATURES_NEEDS_REVIEW:
|
||||
$storage = dt('Needs review');
|
||||
break;
|
||||
}
|
||||
if (
|
||||
($m->status == 0 && ($status == 'all' || $status == 'disabled')) ||
|
||||
($m->status == 1 && ($status == 'all' || $status == 'enabled'))
|
||||
) {
|
||||
$rows[] = array(
|
||||
$m->info['name'],
|
||||
$m->name,
|
||||
$m->status ? dt('Enabled') : dt('Disabled'),
|
||||
$m->info['version'],
|
||||
$storage
|
||||
);
|
||||
}
|
||||
}
|
||||
drush_print_table($rows, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* List components, with pattern matching.
|
||||
*/
|
||||
function drush_features_components() {
|
||||
$args = func_get_args();
|
||||
$components = _drush_features_component_list();
|
||||
// If no args supplied, prompt with a list.
|
||||
if (empty($args)) {
|
||||
$types = array_keys($components);
|
||||
array_unshift($types, 'all');
|
||||
$choice = drush_choice($types, 'Enter a number to choose which component type to list.');
|
||||
if ($choice === FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$args = ($choice == 0) ? array('*') : array($types[$choice]);
|
||||
}
|
||||
$options = array(
|
||||
'provided by' => TRUE,
|
||||
);
|
||||
if (drush_get_option(array('exported', 'e'), NULL)) {
|
||||
$options['not exported'] = FALSE;
|
||||
}
|
||||
elseif (drush_get_option(array('not-exported', 'o'), NULL)) {
|
||||
$options['exported'] = FALSE;
|
||||
}
|
||||
|
||||
$filtered_components = _drush_features_component_filter($components, $args, $options);
|
||||
if ($filtered_components){
|
||||
_drush_features_component_print($filtered_components);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a listing of all known components, indexed by source.
|
||||
*/
|
||||
function _drush_features_component_list() {
|
||||
$components = array();
|
||||
foreach (features_get_feature_components() as $source => $info) {
|
||||
if ($options = features_invoke($source, 'features_export_options')) {
|
||||
foreach ($options as $name => $title) {
|
||||
$components[$source][$name] = $title;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters components by patterns.
|
||||
*/
|
||||
function _drush_features_component_filter($all_components, $patterns = array(), $options = array()) {
|
||||
$options += array(
|
||||
'exported' => TRUE,
|
||||
'not exported' => TRUE,
|
||||
'provided by' => FALSE,
|
||||
);
|
||||
$pool = array();
|
||||
// Maps exported components to feature modules.
|
||||
$components_map = features_get_component_map();
|
||||
// First filter on exported state.
|
||||
foreach ($all_components as $source => $components) {
|
||||
foreach ($components as $name => $title) {
|
||||
$exported = sizeof($components_map[$source][$name]) > 0;
|
||||
if ($exported) {
|
||||
if ($options['exported']) {
|
||||
$pool[$source][$name] = $title;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($options['not exported']) {
|
||||
$pool[$source][$name] = $title;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$state_string = '';
|
||||
|
||||
if (!$options['exported']) {
|
||||
$state_string = 'unexported';
|
||||
}
|
||||
elseif (!$options['not exported']) {
|
||||
$state_string = 'exported';
|
||||
}
|
||||
|
||||
$selected = array();
|
||||
foreach ($patterns as $pattern) {
|
||||
// Rewrite * to %. Let users use both as wildcard.
|
||||
$pattern = strtr($pattern, array('*' => '%'));
|
||||
$sources = array();
|
||||
list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
|
||||
// If source is empty, use a pattern.
|
||||
if ($source_pattern == '') {
|
||||
$source_pattern = '%';
|
||||
}
|
||||
if ($component_pattern == '') {
|
||||
$component_pattern = '%';
|
||||
}
|
||||
|
||||
$preg_source_pattern = strtr(preg_quote($source_pattern, '/'), array('%' => '.*'));
|
||||
$preg_component_pattern = strtr(preg_quote($component_pattern, '/'), array('%' => '.*'));
|
||||
/*
|
||||
* If it isn't a pattern, but a simple string, we don't anchor the
|
||||
* pattern, this allows for abbreviating. Else, we do, as this seems more
|
||||
* natural for patterns.
|
||||
*/
|
||||
if (strpos($source_pattern, '%') !== FALSE) {
|
||||
$preg_source_pattern = '^' . $preg_source_pattern . '$';
|
||||
}
|
||||
if (strpos($component_pattern, '%') !== FALSE) {
|
||||
$preg_component_pattern = '^' . $preg_component_pattern . '$';
|
||||
}
|
||||
$matches = array();
|
||||
|
||||
// Find the sources.
|
||||
$all_sources = array_keys($pool);
|
||||
$matches = preg_grep('/' . $preg_source_pattern . '/', $all_sources);
|
||||
if (sizeof($matches) > 0) {
|
||||
// If we have multiple matches and the source string wasn't a
|
||||
// pattern, check if one of the matches is equal to the pattern, and
|
||||
// use that, or error out.
|
||||
if (sizeof($matches) > 1 and $preg_source_pattern[0] != '^') {
|
||||
if (in_array($source_pattern, $matches)) {
|
||||
$matches = array($source_pattern);
|
||||
}
|
||||
else {
|
||||
return drush_set_error('', dt('Ambiguous source "!source", matches !matches', array('!source' => $source_pattern, '!matches' => join(', ', $matches))));
|
||||
}
|
||||
}
|
||||
// Loose the indexes preg_grep preserved.
|
||||
$sources = array_values($matches);
|
||||
}
|
||||
else {
|
||||
return drush_set_error('', dt('No !state sources match "!source"', array('!state' => $state_string, '!source' => $source_pattern)));
|
||||
}
|
||||
|
||||
|
||||
// Now find the components.
|
||||
foreach ($sources as $source) {
|
||||
// Find the components.
|
||||
$all_components = array_keys($pool[$source]);
|
||||
// See if there's any matches.
|
||||
$matches = preg_grep('/' . $preg_component_pattern . '/', $all_components);
|
||||
if (sizeof($matches) > 0) {
|
||||
// If we have multiple matches and the components string wasn't a
|
||||
// pattern, check if one of the matches is equal to the pattern, and
|
||||
// use that, or error out.
|
||||
if (sizeof($matches) > 1 and $preg_component_pattern[0] != '^') {
|
||||
if (in_array($component_pattern, $matches)) {
|
||||
$matches = array($component_pattern);
|
||||
}
|
||||
else {
|
||||
return drush_set_error('', dt('Ambiguous component "!component", matches !matches', array('!component' => $component_pattern, '!matches' => join(', ', $matches))));
|
||||
}
|
||||
}
|
||||
if (!is_array($selected[$source])) {
|
||||
$selected[$source] = array();
|
||||
}
|
||||
$selected[$source] += array_intersect_key($pool[$source], array_flip($matches));
|
||||
}
|
||||
else {
|
||||
// No matches. If the source was a pattern, just carry on, else
|
||||
// error out. Allows for patterns like :*field*
|
||||
if ($preg_source_pattern[0] != '^') {
|
||||
return drush_set_error('', dt('No !state !source components match "!component"', array('!state' => $state_string, '!component' => $component_pattern, '!source' => $source)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly, provide feature module information on the selected components, if
|
||||
// requested.
|
||||
$provided_by = array();
|
||||
if ($options['provided by'] && $options['exported'] ) {
|
||||
foreach ($selected as $source => $components) {
|
||||
foreach ($components as $name => $title) {
|
||||
$exported = sizeof($components_map[$source][$name]) > 0;
|
||||
if ($exported) {
|
||||
$provided_by[$source . ':' . $name] = join(', ', $components_map[$source][$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'components' => $selected,
|
||||
'sources' => $provided_by,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of filtered components.
|
||||
*/
|
||||
function _drush_features_component_print($filtered_components) {
|
||||
$rows = array(array(dt('Available sources')));
|
||||
foreach ($filtered_components['components'] as $source => $components) {
|
||||
foreach ($components as $name => $value) {
|
||||
$row = array($source .':'. $name);
|
||||
if (isset($filtered_components['sources'][$source .':'. $name])) {
|
||||
$row[] = dt('Provided by') . ': ' . $filtered_components['sources'][$source .':'. $name];
|
||||
}
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
drush_print_table($rows, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a component to a features module, or create a new module with
|
||||
* the selected components.
|
||||
*/
|
||||
function drush_features_export() {
|
||||
if ($args = func_get_args()) {
|
||||
$module = array_shift($args);
|
||||
if (empty($args)) {
|
||||
return drush_set_error('', 'No components supplied.');
|
||||
}
|
||||
$components = _drush_features_component_list();
|
||||
$options = array(
|
||||
'exported' => FALSE,
|
||||
);
|
||||
|
||||
$filtered_components = _drush_features_component_filter($components, $args, $options);
|
||||
$items = $filtered_components['components'];
|
||||
|
||||
if (empty($items)) {
|
||||
return drush_set_error('', 'No components to add.');
|
||||
}
|
||||
|
||||
$items = array_map('array_keys', $items);
|
||||
|
||||
if (($feature = features_load_feature($module, TRUE)) && module_exists($module)) {
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
_features_populate($items, $feature->info, $feature->name);
|
||||
_drush_features_export($feature->info, $feature->name, dirname($feature->filename));
|
||||
}
|
||||
elseif ($feature) {
|
||||
_features_drush_set_error($module, 'FEATURES_FEATURE_NOT_ENABLED');
|
||||
}
|
||||
else {
|
||||
// Same logic as in _drush_features_export. Should be refactored.
|
||||
$destination = drush_get_option(array('destination'), variable_get('features_default_export_path', FEATURES_DEFAULT_EXPORT_PATH));
|
||||
$directory = isset($directory) ? $directory : $destination . '/' . $module;
|
||||
drush_print(dt('Will create a new module in !dir', array('!dir' => $directory)));
|
||||
if (!drush_confirm(dt('Do you really want to continue?'))) {
|
||||
drush_die('Aborting.');
|
||||
}
|
||||
$export = _drush_features_generate_export($items, $module);
|
||||
_features_populate($items, $export[info], $export[name]);
|
||||
_drush_features_export($export['info'], $module, $directory);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return drush_set_error('', 'No feature name given.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a component to a features module
|
||||
* the selected components.
|
||||
*
|
||||
* This is DEPRECATED, but keeping it around for a bit longer for user migration
|
||||
*/
|
||||
function drush_features_add() {
|
||||
drush_print(dt('features-add is DEPRECATED.'));
|
||||
drush_print(dt('Calling features-export instead.'));
|
||||
drush_features_export();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update an existing feature module.
|
||||
*/
|
||||
function drush_features_update() {
|
||||
if ($args = func_get_args()) {
|
||||
foreach ($args as $module) {
|
||||
if (($feature = features_load_feature($module, TRUE)) && module_exists($module)) {
|
||||
_drush_features_export($feature->info, $feature->name, dirname($feature->filename));
|
||||
}
|
||||
elseif ($feature) {
|
||||
_features_drush_set_error($module, 'FEATURES_FEATURE_NOT_ENABLED');
|
||||
}
|
||||
else {
|
||||
_features_drush_set_error($module);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// By default just show contexts that are available.
|
||||
$rows = array(array(dt('Available features')));
|
||||
foreach (features_get_features(NULL, TRUE) as $name => $info) {
|
||||
$rows[] = array($name);
|
||||
}
|
||||
drush_print_table($rows, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all enabled features. Optionally pass in a list of features to
|
||||
* exclude from being updated.
|
||||
*/
|
||||
function drush_features_update_all() {
|
||||
$features_to_update = array();
|
||||
$features_to_exclude = func_get_args();
|
||||
foreach (features_get_features() as $module) {
|
||||
if ($module->status && !in_array($module->name, $features_to_exclude)) {
|
||||
$features_to_update[] = $module->name;
|
||||
}
|
||||
}
|
||||
$dt_args = array('!modules' => implode(', ', $features_to_update));
|
||||
drush_print(dt('The following modules will be updated: !modules', $dt_args));
|
||||
if (!drush_confirm(dt('Do you really want to continue?'))) {
|
||||
return drush_user_abort('Aborting.');
|
||||
}
|
||||
// If we got here, set affirmative to TRUE, so that the user doesn't have to
|
||||
// confirm each and every feature. Start off by storing the current value,
|
||||
// so we can set it back afteward.
|
||||
$skip_confirmation = drush_get_context('DRUSH_AFFIRMATIVE');
|
||||
drush_set_context('DRUSH_AFFIRMATIVE', TRUE);
|
||||
// Now update all the features.
|
||||
drush_invoke('features-update', $features_to_update);
|
||||
// Now set it back as it was, in case other commands are called after this.
|
||||
drush_set_context('DRUSH_AFFIRMATIVE', $skip_confirmation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a module to the site dir.
|
||||
*
|
||||
* @param $info
|
||||
* The feature info associative array.
|
||||
* @param $module_name
|
||||
* Optional. The name for the exported module.
|
||||
*/
|
||||
function _drush_features_export($info, $module_name = NULL, $directory = NULL) {
|
||||
$root = drush_get_option(array('r', 'root'), drush_locate_root());
|
||||
$skip_confirmation = drush_get_context('DRUSH_AFFIRMATIVE');
|
||||
if ($root) {
|
||||
$destination = drush_get_option(array('destination'), variable_get('features_default_export_path', FEATURES_DEFAULT_EXPORT_PATH));
|
||||
$directory = isset($directory) ? $directory : $destination . '/' . $module_name;
|
||||
if (is_dir($directory)) {
|
||||
$warning = dt('Module appears to already exist in !dir', array('!dir' => $directory));
|
||||
drush_log($warning, 'warning');
|
||||
// If we aren't skipping confirmation and the directory already exists,
|
||||
// prompt the user.
|
||||
if (!$skip_confirmation && !drush_confirm(dt('Do you really want to continue?'))) {
|
||||
drush_die('Aborting.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_op('mkdir', $directory);
|
||||
}
|
||||
if (is_dir($directory)) {
|
||||
drupal_flush_all_caches();
|
||||
$export = _drush_features_generate_export($info, $module_name);
|
||||
$files = features_export_render($export, $module_name, TRUE);
|
||||
foreach ($files as $extension => $file_contents) {
|
||||
if (!in_array($extension, array('module', 'info'))) {
|
||||
$extension .= '.inc';
|
||||
}
|
||||
drush_op('file_put_contents', "{$directory}/{$module_name}.$extension", $file_contents);
|
||||
}
|
||||
drush_log(dt("Created module: !module in !directory", array('!module' => $module_name, '!directory' => $directory)), 'ok');
|
||||
}
|
||||
else {
|
||||
drush_die(dt('Couldn\'t create directory !directory', array('!directory' => $directory)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_die(dt('Couldn\'t locate site root'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for _drush_feature_export.
|
||||
*
|
||||
* @param $info
|
||||
* The feature info associative array.
|
||||
* @param $module_name
|
||||
* Optional. The name for the exported module.
|
||||
*/
|
||||
function _drush_features_generate_export(&$info, &$module_name) {
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
$export = features_populate($info, $module_name);
|
||||
if (!features_load_feature($module_name)) {
|
||||
$export['name'] = $module_name;
|
||||
}
|
||||
// Set the feature version if the --version-set or --version-increment option is passed.
|
||||
if ($version = drush_get_option(array('version-set'))) {
|
||||
preg_match('/^(?P<core>\d+\.x)-(?P<major>\d+)\.(?P<patch>\d+)-?(?P<extra>\w+)?$/', $version, $matches);
|
||||
if (!isset($matches['core'], $matches['major'])) {
|
||||
drush_die(dt('Please enter a valid version with core and major version number. Example: !example', array('!example' => '7.x-1.0')));
|
||||
}
|
||||
$export['version'] = $version;
|
||||
}
|
||||
elseif ($version = drush_get_option(array('version-increment'))) {
|
||||
// Determine current version and increment it.
|
||||
$export_load = features_export_prepare($export, $module_name);
|
||||
$version = $export_load['version'];
|
||||
$version_explode = explode('.', $version);
|
||||
$version_minor = array_pop($version_explode);
|
||||
// Increment minor version number if numeric or not a dev release.
|
||||
if (is_numeric($version_minor) || strpos($version_minor, 'dev') !== (strlen($version_minor) - 3)) {
|
||||
// Check for prefixed versions (alpha, beta, rc).
|
||||
if (ctype_digit($version_minor)) {
|
||||
++$version_minor;
|
||||
}
|
||||
else {
|
||||
// Split version number parts.
|
||||
$pattern = '/([0-9]-[a-z]+([0-9])+)/';
|
||||
$matches = array();
|
||||
preg_match($pattern, $version_minor, $matches);
|
||||
$number = array_pop($matches);
|
||||
++$number;
|
||||
$pattern = '/[0-9]+$/';
|
||||
$version_minor = preg_replace($pattern, $number, $version_minor);
|
||||
}
|
||||
}
|
||||
array_push($version_explode, $version_minor);
|
||||
// Rebuild version string.
|
||||
$version = implode('.', $version_explode);
|
||||
$export['version'] = $version;
|
||||
}
|
||||
return $export;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert a feature to it's code definition.
|
||||
* Optionally accept a list of components to revert.
|
||||
*/
|
||||
function drush_features_revert() {
|
||||
if ($args = func_get_args()) {
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
features_include();
|
||||
|
||||
// Determine if revert should be forced.
|
||||
$force = drush_get_option('force');
|
||||
// Determine if -y was supplied. If so, we can filter out needless output
|
||||
// from this command.
|
||||
$skip_confirmation = drush_get_context('DRUSH_AFFIRMATIVE');
|
||||
|
||||
// Parse list of arguments.
|
||||
$modules = array();
|
||||
foreach ($args as $arg) {
|
||||
$arg = explode('.', $arg);
|
||||
$module = array_shift($arg);
|
||||
$component = array_shift($arg);
|
||||
|
||||
if (isset($module)) {
|
||||
if (empty($component)) {
|
||||
// If we received just a feature name, this means that we need all of it's components.
|
||||
$modules[$module] = TRUE;
|
||||
}
|
||||
elseif ($modules[$module] !== TRUE) {
|
||||
if (!isset($modules[$module])) {
|
||||
$modules[$module] = array();
|
||||
}
|
||||
$modules[$module][] = $component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process modules.
|
||||
foreach ($modules as $module => $components_needed) {
|
||||
$dt_args['@module'] = $module;
|
||||
if (($feature = features_load_feature($module, TRUE)) && module_exists($module)) {
|
||||
|
||||
$components = array();
|
||||
// Forcefully revert all components of a feature.
|
||||
if ($force) {
|
||||
foreach (array_keys($feature->info['features']) as $component) {
|
||||
if (features_hook($component, 'features_revert')) {
|
||||
$components[] = $component;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only revert components that are detected to be Overridden/Needs review/rebuildable.
|
||||
else {
|
||||
$states = features_get_component_states(array($feature->name), FALSE);
|
||||
foreach ($states[$feature->name] as $component => $state) {
|
||||
$revertable_states = array(FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW, FEATURES_REBUILDABLE);
|
||||
if (in_array($state, $revertable_states) && features_hook($component, 'features_revert')) {
|
||||
$components[] = $component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($components_needed) && is_array($components_needed)) {
|
||||
$components = array_intersect($components, $components_needed);
|
||||
}
|
||||
if (empty($components)) {
|
||||
drush_log(dt('Current state already matches defaults, aborting.'), 'ok');
|
||||
}
|
||||
else {
|
||||
foreach ($components as $component) {
|
||||
$dt_args['@component'] = $component;
|
||||
$confirmation_message = 'Do you really want to revert @module.@component?';
|
||||
if ($skip_confirmation || drush_confirm(dt($confirmation_message, $dt_args))) {
|
||||
features_revert(array($module => array($component)));
|
||||
drush_log(dt('Reverted @module.@component.', $dt_args), 'ok');
|
||||
}
|
||||
else {
|
||||
drush_log(dt('Skipping @module.@component.', $dt_args), 'ok');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($feature) {
|
||||
_features_drush_set_error($module, 'FEATURES_FEATURE_NOT_ENABLED');
|
||||
}
|
||||
else {
|
||||
_features_drush_set_error($module);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_features_list();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert all enabled features to their definitions in code.
|
||||
*
|
||||
* @param ...
|
||||
* (Optional) A list of features to exclude from being reverted.
|
||||
*/
|
||||
function drush_features_revert_all() {
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
$force = drush_get_option('force');
|
||||
$features_to_exclude = func_get_args();
|
||||
|
||||
$features_to_revert = array();
|
||||
foreach (features_get_features(NULL, TRUE) as $module) {
|
||||
if ($module->status && !in_array($module->name, $features_to_exclude)) {
|
||||
// If forced, add module regardless of status.
|
||||
if ($force) {
|
||||
$features_to_revert[] = $module->name;
|
||||
}
|
||||
else {
|
||||
switch (features_get_storage($module->name)) {
|
||||
case FEATURES_OVERRIDDEN:
|
||||
case FEATURES_NEEDS_REVIEW:
|
||||
case FEATURES_REBUILDABLE:
|
||||
$features_to_revert[] = $module->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($features_to_revert) {
|
||||
$dt_args = array('!modules' => implode(', ', $features_to_revert));
|
||||
drush_print(dt('The following modules will be reverted: !modules', $dt_args));
|
||||
// Confirm that the user would like to continue. If not, simply abort.
|
||||
if (!drush_confirm(dt('Do you really want to continue?'))) {
|
||||
return drush_user_abort('Aborting.');
|
||||
}
|
||||
drush_invoke('features-revert', $features_to_revert);
|
||||
}
|
||||
else {
|
||||
drush_log(dt('Current state already matches defaults, aborting.'), 'ok');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the diff of a feature module.
|
||||
*/
|
||||
function drush_features_diff() {
|
||||
if (!$args = func_get_args()) {
|
||||
drush_features_list();
|
||||
return;
|
||||
}
|
||||
$module = $args[0];
|
||||
$filter_ctypes = drush_get_option("ctypes");
|
||||
if ($filter_ctypes) {
|
||||
$filter_ctypes = explode(',', $filter_ctypes);
|
||||
}
|
||||
|
||||
$feature = features_load_feature($module);
|
||||
if (!module_exists($module)) {
|
||||
drush_log(dt('No such feature is enabled: ' . $module), 'error');
|
||||
return;
|
||||
}
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
$overrides = features_detect_overrides($feature);
|
||||
if (empty($overrides)) {
|
||||
drush_log(dt('Feature is in its default state. No diff needed.'), 'ok');
|
||||
return;
|
||||
}
|
||||
module_load_include('inc', 'diff', 'diff.engine');
|
||||
|
||||
if (!class_exists('DiffFormatter')) {
|
||||
if (drush_confirm(dt('It seems that the Diff module is not available. Would you like to download and enable it?'))) {
|
||||
// Download it if it's not already here.
|
||||
$project_info = drush_get_projects();
|
||||
if (empty($project_info['diff']) && !drush_invoke('dl', array('diff'))) {
|
||||
return drush_set_error(dt('Diff module could not be downloaded.'));
|
||||
}
|
||||
|
||||
if (!drush_invoke('en', array('diff'))) {
|
||||
return drush_set_error(dt('Diff module could not be enabled.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return drush_set_error(dt('Diff module is not enabled.'));
|
||||
}
|
||||
// If we're still here, now we can include the diff.engine again.
|
||||
module_load_include('inc', 'diff', 'diff.engine');
|
||||
}
|
||||
|
||||
$lines = (int) drush_get_option('lines');
|
||||
$lines = $lines > 0 ? $lines : 2;
|
||||
|
||||
$formatter = new DiffFormatter();
|
||||
$formatter->leading_context_lines = $lines;
|
||||
$formatter->trailing_context_lines = $lines;
|
||||
$formatter->show_header = FALSE;
|
||||
|
||||
if (drush_get_context('DRUSH_NOCOLOR')) {
|
||||
$red = $green = "%s";
|
||||
}
|
||||
else {
|
||||
$red = "\033[31;40m\033[1m%s\033[0m";
|
||||
$green = "\033[0;32;40m\033[1m%s\033[0m";
|
||||
}
|
||||
|
||||
// Print key for colors
|
||||
drush_print(dt('Legend: '));
|
||||
drush_print(sprintf($red, dt('Code: drush features-revert will remove the overrides.')));
|
||||
drush_print(sprintf($green, dt('Overrides: drush features-update will update the exported feature with the displayed overrides')));
|
||||
drush_print();
|
||||
|
||||
if ($filter_ctypes) {
|
||||
$overrides = array_intersect_key($overrides, array_flip($filter_ctypes));
|
||||
}
|
||||
foreach ($overrides as $component => $items) {
|
||||
$diff = new Diff(explode("\n", $items['default']), explode("\n", $items['normal']));
|
||||
drush_print();
|
||||
drush_print(dt("Component type: !component", array('!component' => $component)));
|
||||
$rows = explode("\n", $formatter->format($diff));
|
||||
foreach ($rows as $row) {
|
||||
if (strpos($row, '>') === 0) {
|
||||
drush_print(sprintf($green, $row));
|
||||
}
|
||||
elseif (strpos($row, '<') === 0) {
|
||||
drush_print(sprintf($red, $row));
|
||||
}
|
||||
else {
|
||||
drush_print($row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to call drush_set_error().
|
||||
*
|
||||
* @param $feature
|
||||
* The string name of the feature.
|
||||
* @param $error
|
||||
* A text string identifying the type of error.
|
||||
* @return
|
||||
* FALSE. See drush_set_error().
|
||||
*/
|
||||
function _features_drush_set_error($feature, $error = '') {
|
||||
$args = array('!feature' => $feature);
|
||||
|
||||
switch ($error) {
|
||||
case 'FEATURES_FEATURE_NOT_ENABLED':
|
||||
$message = 'The feature !feature is not enabled.';
|
||||
break;
|
||||
case 'FEATURES_COMPONENT_NOT_FOUND':
|
||||
$message = 'The given component !feature could not be found.';
|
||||
break;
|
||||
default:
|
||||
$error = 'FEATURES_FEATURE_NOT_FOUND';
|
||||
$message = 'The feature !feature could not be found.';
|
||||
}
|
||||
|
||||
return drush_set_error($error, dt($message, $args));
|
||||
}
|
1015
sites/all/modules/contrib/admin/features/features.export.inc
Normal file
1015
sites/all/modules/contrib/admin/features/features.export.inc
Normal file
File diff suppressed because it is too large
Load Diff
14
sites/all/modules/contrib/admin/features/features.info
Normal file
14
sites/all/modules/contrib/admin/features/features.info
Normal file
@@ -0,0 +1,14 @@
|
||||
name = "Features"
|
||||
description = "Provides feature management for Drupal."
|
||||
core = 7.x
|
||||
package = "Features"
|
||||
files[] = tests/features.test
|
||||
|
||||
configure = admin/structure/features/settings
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-10-17
|
||||
version = "7.x-2.0+0-dev"
|
||||
core = "7.x"
|
||||
project = "features"
|
||||
datestamp = "1382036080"
|
||||
|
122
sites/all/modules/contrib/admin/features/features.install
Normal file
122
sites/all/modules/contrib/admin/features/features.install
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the features module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function features_install() {
|
||||
_features_install_menu();
|
||||
db_update('system')
|
||||
->fields(array('weight' => 20))
|
||||
->condition('name', 'features')
|
||||
->condition('type', 'module')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function features_uninstall() {
|
||||
variable_del('features_codecache');
|
||||
variable_del('features_default_export_path');
|
||||
variable_del('features_semaphore');
|
||||
variable_del('features_ignored_orphans');
|
||||
if (db_table_exists('menu_custom')) {
|
||||
db_delete('menu_custom')
|
||||
->condition('menu_name', 'features')
|
||||
->execute();
|
||||
}
|
||||
db_delete('menu_links')
|
||||
->condition('module', 'features')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create menu. See menu.install for an example.
|
||||
*/
|
||||
function _features_install_menu() {
|
||||
if (db_table_exists('menu_custom') && !db_query("SELECT menu_name FROM {menu_custom} WHERE menu_name = :menu_name", array(':menu_name' => 'features'))->fetchField()) {
|
||||
$t = get_t();
|
||||
$id = db_insert('menu_custom')
|
||||
->fields(array(
|
||||
'menu_name' => 'features',
|
||||
'title' => $t('Features'),
|
||||
'description' => $t('Menu items for any enabled features.'),
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update 6100: Set module on all feature node types to 'features'.
|
||||
|
||||
* This update can be re-run as needed to repair any node types that are not
|
||||
* removed after disabling the associated feature.
|
||||
*
|
||||
* Any feature implementing a node component that was exported prior to this
|
||||
* version of the features.module will need to have its 'module' declaration
|
||||
* in hook_node_info() changed from 'node' to 'features'.
|
||||
*/
|
||||
function features_update_6100() {
|
||||
$ret = array();
|
||||
|
||||
foreach (features_get_features(NULL, TRUE) as $feature) {
|
||||
if (module_exists($feature->name) && $types = module_invoke($feature->name, 'node_info')) {
|
||||
foreach ($types as $type => $type_data) {
|
||||
$sql = "SELECT COUNT(*) FROM {node_type} WHERE module = 'node' AND type = '%s'";
|
||||
// Only update if the hook_node_info type's module is 'features' and the db type's
|
||||
// module is 'node'.
|
||||
if (($type_data['module'] == 'features') && db_query($sql, $type)->fetchField()) {
|
||||
$ret[] = update_sql("UPDATE {node_type} SET module = 'features' WHERE type = '$type'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update 6101: Set codestate signature for all features.
|
||||
*
|
||||
* This update generates a codestate for all feature/component pairs which
|
||||
* have been installed prior to this version of Features. This prevents
|
||||
* automatic rebuilds from occurring against any rebuildable components
|
||||
* that have been overridden.
|
||||
*/
|
||||
function features_update_6101() {
|
||||
// Ensure all of our own API functions still exist in in this version
|
||||
// of Features. It's possible that the "future me" will not have these
|
||||
// functions, so I should check.
|
||||
module_load_include('inc', 'features', "features.export");
|
||||
$functions = array(
|
||||
'features_include',
|
||||
'features_hook',
|
||||
'features_get_components',
|
||||
'features_get_features',
|
||||
'features_get_signature',
|
||||
'features_set_signature',
|
||||
);
|
||||
$doit = TRUE;
|
||||
foreach ($functions as $function) {
|
||||
$doit = $doit && function_exists($function);
|
||||
}
|
||||
if ($doit) {
|
||||
features_include();
|
||||
$features = array_keys(features_get_features(NULL, TRUE));
|
||||
$components = array_keys(features_get_components());
|
||||
foreach ($features as $feature) {
|
||||
if (module_exists($feature)) {
|
||||
foreach ($components as $component) {
|
||||
if (features_hook($component, 'features_rebuild') && features_get_signature('cache', $feature, $component) === FALSE) {
|
||||
features_set_signature($feature, $component, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
457
sites/all/modules/contrib/admin/features/features.js
Normal file
457
sites/all/modules/contrib/admin/features/features.js
Normal file
@@ -0,0 +1,457 @@
|
||||
/**
|
||||
* jQuery.fn.sortElements
|
||||
* --------------
|
||||
* @param Function comparator:
|
||||
* Exactly the same behaviour as [1,2,3].sort(comparator)
|
||||
*
|
||||
* @param Function getSortable
|
||||
* A function that should return the element that is
|
||||
* to be sorted. The comparator will run on the
|
||||
* current collection, but you may want the actual
|
||||
* resulting sort to occur on a parent or another
|
||||
* associated element.
|
||||
*
|
||||
* E.g. $('td').sortElements(comparator, function(){
|
||||
* return this.parentNode;
|
||||
* })
|
||||
*
|
||||
* The <td>'s parent (<tr>) will be sorted instead
|
||||
* of the <td> itself.
|
||||
*
|
||||
* Credit: http://james.padolsey.com/javascript/sorting-elements-with-jquery/
|
||||
*
|
||||
*/
|
||||
jQuery.fn.sortElements = (function(){
|
||||
|
||||
var sort = [].sort;
|
||||
|
||||
return function(comparator, getSortable) {
|
||||
|
||||
getSortable = getSortable || function(){return this;};
|
||||
|
||||
var placements = this.map(function(){
|
||||
|
||||
var sortElement = getSortable.call(this),
|
||||
parentNode = sortElement.parentNode,
|
||||
|
||||
// Since the element itself will change position, we have
|
||||
// to have some way of storing its original position in
|
||||
// the DOM. The easiest way is to have a 'flag' node:
|
||||
nextSibling = parentNode.insertBefore(
|
||||
document.createTextNode(''),
|
||||
sortElement.nextSibling
|
||||
);
|
||||
|
||||
return function() {
|
||||
|
||||
if (parentNode === this) {
|
||||
throw new Error(
|
||||
"You can't sort elements if any one is a descendant of another."
|
||||
);
|
||||
}
|
||||
|
||||
// Insert before flag:
|
||||
parentNode.insertBefore(this, nextSibling);
|
||||
// Remove flag:
|
||||
parentNode.removeChild(nextSibling);
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
return sort.call(this, comparator).each(function(i){
|
||||
placements[i].call(getSortable.call(this));
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
(function ($) {
|
||||
Drupal.behaviors.features = {
|
||||
attach: function(context, settings) {
|
||||
// Features management form
|
||||
$('table.features:not(.processed)', context).each(function() {
|
||||
$(this).addClass('processed');
|
||||
|
||||
// Check the overridden status of each feature
|
||||
Drupal.features.checkStatus();
|
||||
|
||||
// Add some nicer row hilighting when checkboxes change values
|
||||
$('input', this).bind('change', function() {
|
||||
if (!$(this).attr('checked')) {
|
||||
$(this).parents('tr').removeClass('enabled').addClass('disabled');
|
||||
}
|
||||
else {
|
||||
$(this).parents('tr').addClass('enabled').removeClass('disabled');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Export form component selector
|
||||
$('form.features-export-form select.features-select-components:not(.processed)', context).each(function() {
|
||||
$(this)
|
||||
.addClass('processed')
|
||||
.change(function() {
|
||||
var target = $(this).val();
|
||||
$('div.features-select').hide();
|
||||
$('div.features-select-' + target).show();
|
||||
return false;
|
||||
}).trigger('change');
|
||||
});
|
||||
|
||||
// Export form machine-readable JS
|
||||
$('.feature-name:not(.processed)', context).each(function() {
|
||||
$('.feature-name')
|
||||
.addClass('processed')
|
||||
.after(' <small class="feature-module-name-suffix"> </small>');
|
||||
if ($('.feature-module-name').val() === $('.feature-name').val().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/_+/g, '_') || $('.feature-module-name').val() === '') {
|
||||
$('.feature-module-name').parents('.form-item').hide();
|
||||
$('.feature-name').bind('keyup change', function() {
|
||||
var machine = $(this).val().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/_+/g, '_');
|
||||
if (machine !== '_' && machine !== '') {
|
||||
$('.feature-module-name').val(machine);
|
||||
$('.feature-module-name-suffix').empty().append(' Machine name: ' + machine + ' [').append($('<a href="#">'+ Drupal.t('Edit') +'</a>').click(function() {
|
||||
$('.feature-module-name').parents('.form-item').show();
|
||||
$('.feature-module-name-suffix').hide();
|
||||
$('.feature-name').unbind('keyup');
|
||||
return false;
|
||||
})).append(']');
|
||||
}
|
||||
else {
|
||||
$('.feature-module-name').val(machine);
|
||||
$('.feature-module-name-suffix').text('');
|
||||
}
|
||||
});
|
||||
$('.feature-name').keyup();
|
||||
}
|
||||
});
|
||||
|
||||
//View info dialog
|
||||
var infoDialog = $('#features-info-file');
|
||||
if (infoDialog.length != 0) {
|
||||
infoDialog.dialog({
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
draggable: false,
|
||||
resizable: false,
|
||||
width: 600,
|
||||
height: 480
|
||||
});
|
||||
}
|
||||
|
||||
if ((Drupal.settings.features != undefined) && (Drupal.settings.features.info != undefined)) {
|
||||
$('#features-info-file textarea').val(Drupal.settings.features.info);
|
||||
$('#features-info-file').dialog('open');
|
||||
//To be reset by the button click ajax
|
||||
Drupal.settings.features.info = undefined;
|
||||
}
|
||||
|
||||
// mark any conflicts with a class
|
||||
if ((Drupal.settings.features != undefined) && (Drupal.settings.features.conflicts != undefined)) {
|
||||
for (var moduleName in Drupal.settings.features.conflicts) {
|
||||
moduleConflicts = Drupal.settings.features.conflicts[moduleName];
|
||||
$('#features-export-wrapper input[type=checkbox]', context).each(function() {
|
||||
if (!$(this).hasClass('features-checkall')) {
|
||||
var key = $(this).attr('name');
|
||||
var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
|
||||
var component = matches[1];
|
||||
var item = matches[4];
|
||||
if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) {
|
||||
$(this).parent().addClass('features-conflict');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _checkAll(value) {
|
||||
if (value) {
|
||||
$('#features-export-wrapper .component-select input[type=checkbox]:visible', context).each(function() {
|
||||
var move_id = $(this).attr('id');
|
||||
$(this).click();
|
||||
$('#'+ move_id).attr('checked', 'checked');
|
||||
});
|
||||
}
|
||||
else {
|
||||
$('#features-export-wrapper .component-added input[type=checkbox]:visible', context).each(function() {
|
||||
var move_id = $(this).attr('id');
|
||||
$('#'+ move_id).removeAttr('checked');
|
||||
$(this).click();
|
||||
$('#'+ move_id).removeAttr('checked');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateComponentCountInfo(item, section) {
|
||||
console.log(section);
|
||||
switch (section) {
|
||||
case 'select':
|
||||
var parent = $(item).closest('.features-export-list').siblings('.features-export-component');
|
||||
$('.component-count', parent).text(function (index, text) {
|
||||
return +text + 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 'added':
|
||||
case 'detected':
|
||||
var parent = $(item).closest('.features-export-component');
|
||||
$('.component-count', parent).text(function (index, text) {
|
||||
return text - 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function moveCheckbox(item, section, value) {
|
||||
updateComponentCountInfo(item, section);
|
||||
var curParent = item;
|
||||
if ($(item).hasClass('form-type-checkbox')) {
|
||||
item = $(item).children('input[type=checkbox]');
|
||||
}
|
||||
else {
|
||||
curParent = $(item).parents('.form-type-checkbox');
|
||||
}
|
||||
var newParent = $(curParent).parents('.features-export-parent').find('.form-checkboxes.component-'+section);
|
||||
$(curParent).detach();
|
||||
$(curParent).appendTo(newParent);
|
||||
var list = ['select', 'added', 'detected', 'included'];
|
||||
for (i in list) {
|
||||
$(curParent).removeClass('component-' + list[i]);
|
||||
$(item).removeClass('component-' + list[i]);
|
||||
}
|
||||
$(curParent).addClass('component-'+section);
|
||||
$(item).addClass('component-'+section);
|
||||
if (value) {
|
||||
$(item).attr('checked', 'checked');
|
||||
}
|
||||
else {
|
||||
$(item).removeAttr('checked')
|
||||
}
|
||||
$(newParent).parent().removeClass('features-export-empty');
|
||||
|
||||
// re-sort new list of checkboxes based on labels
|
||||
$(newParent).find('label').sortElements(
|
||||
function(a, b){
|
||||
return $(a).text() > $(b).text() ? 1 : -1;
|
||||
},
|
||||
function(){
|
||||
return this.parentNode;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// provide timer for auto-refresh trigger
|
||||
var timeoutID = 0;
|
||||
var inTimeout = 0;
|
||||
function _triggerTimeout() {
|
||||
timeoutID = 0;
|
||||
_updateDetected();
|
||||
}
|
||||
function _resetTimeout() {
|
||||
inTimeout++;
|
||||
// if timeout is already active, reset it
|
||||
if (timeoutID != 0) {
|
||||
window.clearTimeout(timeoutID);
|
||||
if (inTimeout > 0) inTimeout--;
|
||||
}
|
||||
timeoutID = window.setTimeout(_triggerTimeout, 500);
|
||||
}
|
||||
|
||||
function _updateDetected() {
|
||||
var autodetect = $('#features-autodetect input[type=checkbox]');
|
||||
if ((autodetect.length > 0) && (!autodetect.is(':checked'))) return;
|
||||
// query the server for a list of components/items in the feature and update
|
||||
// the auto-detected items
|
||||
var items = []; // will contain a list of selected items exported to feature
|
||||
var components = {}; // contains object of component names that have checked items
|
||||
$('#features-export-wrapper input[type=checkbox]:checked', context).each(function() {
|
||||
if (!$(this).hasClass('features-checkall')) {
|
||||
var key = $(this).attr('name');
|
||||
var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
|
||||
components[matches[1]] = matches[1];
|
||||
if (!$(this).hasClass('component-detected')) {
|
||||
items.push(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
var featureName = $('#edit-module-name').val();
|
||||
if (featureName == '') {
|
||||
featureName = '*';
|
||||
}
|
||||
var url = Drupal.settings.basePath + 'features/ajaxcallback/' + featureName;
|
||||
var excluded = Drupal.settings.features.excluded;
|
||||
var postData = {'items': items, 'excluded': excluded};
|
||||
jQuery.post(url, postData, function(data) {
|
||||
if (inTimeout > 0) inTimeout--;
|
||||
// if we have triggered another timeout then don't update with old results
|
||||
if (inTimeout == 0) {
|
||||
// data is an object keyed by component listing the exports of the feature
|
||||
for (var component in data) {
|
||||
var itemList = data[component];
|
||||
$('#features-export-wrapper .component-' + component + ' input[type=checkbox]', context).each(function() {
|
||||
var key = $(this).attr('value');
|
||||
// first remove any auto-detected items that are no longer in component
|
||||
if ($(this).hasClass('component-detected')) {
|
||||
if (!(key in itemList)) {
|
||||
moveCheckbox(this, 'select', false)
|
||||
}
|
||||
}
|
||||
// next, add any new auto-detected items
|
||||
else if ($(this).hasClass('component-select')) {
|
||||
if (key in itemList) {
|
||||
moveCheckbox(this, 'detected', itemList[key]);
|
||||
$(this).parent().show(); // make sure it's not hidden from filter
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// loop over all selected components and check for any that have been completely removed
|
||||
for (var component in components) {
|
||||
if ((data == null) || !(component in data)) {
|
||||
$('#features-export-wrapper .component-' + component + ' input[type=checkbox].component-detected', context).each(function() {
|
||||
moveCheckbox(this, 'select', false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "json");
|
||||
}
|
||||
|
||||
// Handle component selection UI
|
||||
$('#features-export-wrapper input[type=checkbox]', context).click(function() {
|
||||
_resetTimeout();
|
||||
if ($(this).hasClass('component-select')) {
|
||||
moveCheckbox(this, 'added', true);
|
||||
}
|
||||
else if ($(this).hasClass('component-included')) {
|
||||
moveCheckbox(this, 'added', false);
|
||||
}
|
||||
else if ($(this).hasClass('component-added')) {
|
||||
if ($(this).is(':checked')) {
|
||||
moveCheckbox(this, 'included', true);
|
||||
}
|
||||
else {
|
||||
moveCheckbox(this, 'select', false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle select/unselect all
|
||||
$('#features-filter .features-checkall', context).click(function() {
|
||||
if ($(this).attr('checked')) {
|
||||
_checkAll(true);
|
||||
$(this).next().html(Drupal.t('Deselect all'));
|
||||
}
|
||||
else {
|
||||
_checkAll(false);
|
||||
$(this).next().html(Drupal.t('Select all'));
|
||||
}
|
||||
_resetTimeout();
|
||||
});
|
||||
|
||||
// Handle filtering
|
||||
|
||||
// provide timer for auto-refresh trigger
|
||||
var filterTimeoutID = 0;
|
||||
var inFilterTimeout = 0;
|
||||
function _triggerFilterTimeout() {
|
||||
filterTimeoutID = 0;
|
||||
_updateFilter();
|
||||
}
|
||||
function _resetFilterTimeout() {
|
||||
inFilterTimeout++;
|
||||
// if timeout is already active, reset it
|
||||
if (filterTimeoutID != 0) {
|
||||
window.clearTimeout(filterTimeoutID);
|
||||
if (inFilterTimeout > 0) inFilterTimeout--;
|
||||
}
|
||||
filterTimeoutID = window.setTimeout(_triggerFilterTimeout, 200);
|
||||
}
|
||||
function _updateFilter() {
|
||||
var filter = $('#features-filter input').val();
|
||||
var regex = new RegExp(filter, 'i');
|
||||
// collapse fieldsets
|
||||
var newState = {};
|
||||
var currentState = {};
|
||||
$('#features-export-wrapper fieldset.features-export-component', context).each(function() {
|
||||
// expand parent fieldset
|
||||
var section = $(this).attr('id');
|
||||
currentState[section] = !($(this).hasClass('collapsed'));
|
||||
if (!(section in newState)) {
|
||||
newState[section] = false;
|
||||
}
|
||||
|
||||
$(this).find('div.component-select label').each(function() {
|
||||
if (filter == '') {
|
||||
if (currentState[section]) {
|
||||
Drupal.toggleFieldset($('#'+section));
|
||||
currentState[section] = false;
|
||||
}
|
||||
$(this).parent().show();
|
||||
}
|
||||
else if ($(this).text().match(regex)) {
|
||||
$(this).parent().show();
|
||||
newState[section] = true;
|
||||
}
|
||||
else {
|
||||
$(this).parent().hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
for (section in newState) {
|
||||
if (currentState[section] != newState[section]) {
|
||||
Drupal.toggleFieldset($('#'+section));
|
||||
}
|
||||
}
|
||||
}
|
||||
$('#features-filter input', context).bind("input", function() {
|
||||
_resetFilterTimeout();
|
||||
});
|
||||
$('#features-filter .features-filter-clear', context).click(function() {
|
||||
$('#features-filter input').val('');
|
||||
_updateFilter();
|
||||
});
|
||||
|
||||
// show the filter bar
|
||||
$('#features-filter', context).removeClass('element-invisible');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Drupal.features = {
|
||||
'checkStatus': function() {
|
||||
$('table.features tbody tr').not('.processed').filter(':first').each(function() {
|
||||
var elem = $(this);
|
||||
$(elem).addClass('processed');
|
||||
var uri = $(this).find('a.admin-check').attr('href');
|
||||
if (uri) {
|
||||
$.get(uri, [], function(data) {
|
||||
$(elem).find('.admin-loading').hide();
|
||||
switch (data.storage) {
|
||||
case 3:
|
||||
$(elem).find('.admin-rebuilding').show();
|
||||
break;
|
||||
case 2:
|
||||
$(elem).find('.admin-needs-review').show();
|
||||
break;
|
||||
case 1:
|
||||
$(elem).find('.admin-overridden').show();
|
||||
break;
|
||||
default:
|
||||
$(elem).find('.admin-default').show();
|
||||
break;
|
||||
}
|
||||
Drupal.features.checkStatus();
|
||||
}, 'json');
|
||||
}
|
||||
else {
|
||||
Drupal.features.checkStatus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
})(jQuery);
|
||||
|
||||
|
1111
sites/all/modules/contrib/admin/features/features.module
Normal file
1111
sites/all/modules/contrib/admin/features/features.module
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function block_features_api() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function block_features_export($data, &$export) {
|
||||
$pipe = array();
|
||||
foreach ($data as $bid) {
|
||||
$split = explode('-', $bid);
|
||||
$module = array_shift($split);
|
||||
$delta = implode('-', $split);
|
||||
|
||||
$export['dependencies'][$module] = $module;
|
||||
|
||||
switch ($module) {
|
||||
case 'views':
|
||||
if (strlen($delta) == 32) {
|
||||
$hashes = variable_get('views_block_hashes', array());
|
||||
if (!empty($hashes[$delta])) {
|
||||
$delta = $hashes[$delta];
|
||||
}
|
||||
}
|
||||
|
||||
$delta_split = explode('-', $delta);
|
||||
$view_name = $delta_split[0];
|
||||
if (!empty($view_name)) {
|
||||
$pipe['views'][] = $view_name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function context_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = ctools_component_features_export('context', $data, $export, $module_name);
|
||||
|
||||
$contexts = context_load();
|
||||
foreach ($data as $identifier) {
|
||||
if (isset($contexts[$identifier])) {
|
||||
$context = $contexts[$identifier];
|
||||
// Conditions.
|
||||
// Currently only node and views conditions are supported.
|
||||
// @TODO: Should this be delegated to a method on the plugin?
|
||||
foreach (array('node', 'views') as $key) {
|
||||
if (!empty($context->conditions{$key}['values'])) {
|
||||
foreach ($context->conditions{$key}['values'] as $item) {
|
||||
// Special pipe for views
|
||||
if ($key === 'views') {
|
||||
$split = explode(':', $item);
|
||||
$view_name = array_shift($split);
|
||||
$pipe[$key][$view_name] = $view_name;
|
||||
}
|
||||
else {
|
||||
$pipe[$key][$item] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reactions.
|
||||
if (!empty($context->reactions['block']['blocks'])) {
|
||||
foreach ($context->reactions['block']['blocks'] as $block) {
|
||||
$block = (array) $block;
|
||||
$bid = "{$block['module']}-{$block['delta']}";
|
||||
$pipe['block'][$bid] = $bid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*
|
||||
* @param $module
|
||||
* name of module to revert content for
|
||||
*/
|
||||
function context_features_revert($module = NULL) {
|
||||
$return = ctools_component_features_revert('context', $module);
|
||||
context_invalidate_cache();
|
||||
return $return;
|
||||
}
|
@@ -0,0 +1,378 @@
|
||||
<?php
|
||||
|
||||
function ctools_features_declare_functions($reset = FALSE) {
|
||||
/**
|
||||
* This is called by Features to ensure ctools component functions are defined
|
||||
* Dynamically declare functions under a ctools component's namespace if they are not already declared.
|
||||
*/
|
||||
if (function_exists('_ctools_features_get_info')) {
|
||||
foreach (_ctools_features_get_info(NULL, $reset) as $component => $info) {
|
||||
$code = '';
|
||||
if (!function_exists("{$info['module']}_features_api")) {
|
||||
$code .= 'function '. $info['module'] .'_features_api() { return ctools_component_features_api("'. $info['module'] .'"); }';
|
||||
}
|
||||
|
||||
// ctools component with owner defined as "ctools"
|
||||
if (!function_exists("{$component}_features_api") && $info['module'] === 'ctools') {
|
||||
$code .= 'function '. $component .'_features_api() { return ctools_component_features_api("'. $component .'"); }';
|
||||
}
|
||||
|
||||
if (!function_exists("{$component}_features_export")) {
|
||||
$code .= 'function '. $component .'_features_export($data, &$export, $module_name = "") { return ctools_component_features_export("'. $component .'", $data, $export, $module_name); }';
|
||||
}
|
||||
if (!function_exists("{$component}_features_export_options")) {
|
||||
$code .= 'function '. $component .'_features_export_options() { return ctools_component_features_export_options("'. $component .'"); }';
|
||||
}
|
||||
if (!function_exists("{$component}_features_export_render")) {
|
||||
$code .= 'function '. $component .'_features_export_render($module, $data, $export = NULL) { return ctools_component_features_export_render("'. $component .'", $module, $data, $export); }';
|
||||
}
|
||||
if (!function_exists("{$component}_features_revert")) {
|
||||
$code .= 'function '. $component .'_features_revert($module) { return ctools_component_features_revert("'. $component .'", $module); }';
|
||||
}
|
||||
eval($code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function ctools_features_api() {
|
||||
return array(
|
||||
'ctools' => array(
|
||||
'name' => 'CTools export API',
|
||||
'feature_source' => TRUE,
|
||||
'duplicates' => FEATURES_DUPLICATES_ALLOWED,
|
||||
// CTools API integration does not include a default hook declaration as
|
||||
// it is not a proper default hook.
|
||||
// 'default_hook' => 'ctools_plugin_api',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
* Adds references to the ctools mothership hook, ctools_plugin_api().
|
||||
*/
|
||||
function ctools_features_export($data, &$export, $module_name = '') {
|
||||
// Add ctools dependency
|
||||
$export['dependencies']['ctools'] = 'ctools';
|
||||
|
||||
// Add the actual ctools components which will need to be accounted for in
|
||||
// hook_ctools_plugin_api(). The components are actually identified by a
|
||||
// delimited list of values: `module_name:api:current_version`
|
||||
foreach ($data as $component) {
|
||||
if ($info = _ctools_features_get_info($component)) {
|
||||
$identifier = "{$info['module']}:{$info['api']}:{$info['current_version']}";
|
||||
$export['features']['ctools'][$identifier] = $identifier;
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
* Adds the ctools mothership hook, ctools_plugin_api().
|
||||
*/
|
||||
function ctools_features_export_render($module, $data) {
|
||||
$component_exports = array();
|
||||
foreach ($data as $component) {
|
||||
$code = array();
|
||||
if ($info = _ctools_features_get_info($component)) {
|
||||
// For background on why we change the output for hook_views_api()
|
||||
// see http://drupal.org/node/1459120.
|
||||
if ($info['module'] == 'views') {
|
||||
$code[] = ' return array("api" => "3.0");';
|
||||
}
|
||||
else {
|
||||
$code[] = ' if ($module == "'. $info['module'] .'" && $api == "'. $info['api'] .'") {';
|
||||
$code[] = ' return array("version" => "'. $info['current_version'] .'");';
|
||||
$code[] = ' }';
|
||||
}
|
||||
}
|
||||
ctools_include('plugins');
|
||||
$plugin_api_hook_name = ctools_plugin_api_get_hook($info['module'], $info['api']);
|
||||
|
||||
if (key_exists($plugin_api_hook_name, $component_exports)) {
|
||||
$component_exports[$plugin_api_hook_name]['code'] .= "\n" . implode("\n", $code);
|
||||
}
|
||||
else {
|
||||
$component_exports[$plugin_api_hook_name] = array(
|
||||
'code' => implode("\n", $code),
|
||||
'args' => '$module = NULL, $api = NULL',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $component_exports;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Master implementation of hook_features_api() for all ctools components.
|
||||
*
|
||||
* Note that this master hook does not use $component like the others, but uses the
|
||||
* component module's namespace instead.
|
||||
*/
|
||||
function ctools_component_features_api($module_name) {
|
||||
$api = array();
|
||||
foreach (_ctools_features_get_info() as $component => $info) {
|
||||
// if module owner is set to "ctools" we need to compare the component
|
||||
if ($info['module'] == $module_name || ($info['module'] === 'ctools' && $component == $module_name) ) {
|
||||
$api[$component] = $info;
|
||||
}
|
||||
}
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Master implementation of hook_features_export_options() for all ctools components.
|
||||
*/
|
||||
function ctools_component_features_export_options($component) {
|
||||
$options = array();
|
||||
|
||||
ctools_include('export');
|
||||
$schema = ctools_export_get_schema($component);
|
||||
if ($schema && $schema['export']['bulk export']) {
|
||||
if (!empty($schema['export']['list callback']) && function_exists($schema['export']['list callback'])) {
|
||||
$options = $schema['export']['list callback']();
|
||||
}
|
||||
else {
|
||||
$options = _ctools_features_export_default_list($component, $schema);
|
||||
}
|
||||
}
|
||||
asort($options);
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Master implementation of hook_features_export() for all ctools components.
|
||||
*/
|
||||
function ctools_component_features_export($component, $data, &$export, $module_name = '') {
|
||||
// Add the actual implementing module as a dependency
|
||||
$info = _ctools_features_get_info();
|
||||
if ($module_name !== $info[$component]['module']) {
|
||||
$export['dependencies'][$info[$component]['module']] = $info[$component]['module'];
|
||||
}
|
||||
|
||||
// Add the components
|
||||
foreach ($data as $object_name) {
|
||||
if ($object = _ctools_features_export_crud_load($component, $object_name)) {
|
||||
// If this object is provided as a default by a different module, don't
|
||||
// export and add that module as a dependency instead.
|
||||
if (!empty($object->export_module) && $object->export_module !== $module_name) {
|
||||
$export['dependencies'][$object->export_module] = $object->export_module;
|
||||
if (isset($export['features'][$component][$object_name])) {
|
||||
unset($export['features'][$component][$object_name]);
|
||||
}
|
||||
}
|
||||
// Otherwise, add the component.
|
||||
else {
|
||||
$export['features'][$component][$object_name] = $object_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Let CTools handle API integration for this component.
|
||||
return array('ctools' => array($component));
|
||||
}
|
||||
|
||||
/**
|
||||
* Master implementation of hook_features_export_render() for all ctools components.
|
||||
*/
|
||||
function ctools_component_features_export_render($component, $module, $data) {
|
||||
// Reset the export display static to prevent clashes.
|
||||
drupal_static_reset('panels_export_display');
|
||||
|
||||
ctools_include('export');
|
||||
$schema = ctools_export_get_schema($component);
|
||||
|
||||
if (function_exists($schema['export']['to hook code callback'])) {
|
||||
$export = $schema['export']['to hook code callback']($data, $module);
|
||||
$code = explode("{\n", $export);
|
||||
array_shift($code);
|
||||
$code = explode('}', implode($code, "{\n"));
|
||||
array_pop($code);
|
||||
$code = implode('}', $code);
|
||||
}
|
||||
else {
|
||||
$code = ' $export = array();'."\n\n";
|
||||
foreach ($data as $object_name) {
|
||||
if ($object = _ctools_features_export_crud_load($component, $object_name)) {
|
||||
$identifier = $schema['export']['identifier'];
|
||||
$code .= _ctools_features_export_crud_export($component, $object, ' ');
|
||||
$code .= " \$export[" . ctools_var_export($object_name) . "] = \${$identifier};\n\n";
|
||||
}
|
||||
}
|
||||
$code .= ' return $export;';
|
||||
}
|
||||
|
||||
return array($schema['export']['default hook'] => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Master implementation of hook_features_revert() for all ctools components.
|
||||
*/
|
||||
function ctools_component_features_revert($component, $module) {
|
||||
if ($objects = features_get_default($component, $module)) {
|
||||
foreach ($objects as $name => $object) {
|
||||
// Some things (like views) do not use the machine name as key
|
||||
// and need to be loaded explicitly in order to be deleted.
|
||||
$object = ctools_export_crud_load($component, $name);
|
||||
if ($object && ($object->export_type & EXPORT_IN_DATABASE)) {
|
||||
_ctools_features_export_crud_delete($component, $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return various ctools information for components.
|
||||
*/
|
||||
function _ctools_features_get_info($identifier = NULL, $reset = FALSE) {
|
||||
static $components;
|
||||
if (!isset($components) || $reset) {
|
||||
$components = array();
|
||||
$modules = features_get_info();
|
||||
ctools_include('export');
|
||||
drupal_static('ctools_export_get_schemas', NULL, $reset);
|
||||
foreach (ctools_export_get_schemas_by_module() as $module => $schemas) {
|
||||
foreach ($schemas as $table => $schema) {
|
||||
if ($schema['export']['bulk export']) {
|
||||
// Let the API owner take precedence as the owning module.
|
||||
$api_module = isset($schema['export']['api']['owner']) ? $schema['export']['api']['owner'] : $module;
|
||||
$components[$table] = array(
|
||||
'name' => isset($modules[$api_module]->info['name']) ? $modules[$api_module]->info['name'] : $api_module,
|
||||
'default_hook' => $schema['export']['default hook'],
|
||||
'default_file' => FEATURES_DEFAULTS_CUSTOM,
|
||||
'module' => $api_module,
|
||||
'feature_source' => TRUE,
|
||||
);
|
||||
if (isset($schema['export']['api'])) {
|
||||
$components[$table] += array(
|
||||
'api' => $schema['export']['api']['api'],
|
||||
'default_filename' => $schema['export']['api']['api'],
|
||||
'current_version' => $schema['export']['api']['current_version'],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return information specific to a particular component.
|
||||
if (isset($identifier)) {
|
||||
// Identified by the table name.
|
||||
if (isset($components[$identifier])) {
|
||||
return $components[$identifier];
|
||||
}
|
||||
// New API identifier. Allows non-exportables related CTools APIs to be
|
||||
// supported by an explicit `module:api:current_version` key.
|
||||
else if (substr_count($identifier, ':') === 2) {
|
||||
list($module, $api, $current_version) = explode(':', $identifier);
|
||||
// If a schema component matches the provided identifier, provide that
|
||||
// information. This also ensures that the version number is up to date.
|
||||
foreach ($components as $table => $info) {
|
||||
if ($info['module'] == $module && $info['api'] == $api && $info['current_version'] >= $current_version) {
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
// Fallback to just giving back what was provided to us.
|
||||
return array('module' => $module, 'api' => $api, 'current_version' => $current_version);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around ctools_export_crud_export() for < 1.7 compatibility.
|
||||
*/
|
||||
function _ctools_features_export_crud_export($table, $object, $indent = '') {
|
||||
return ctools_api_version('1.7') ? ctools_export_crud_export($table, $object, $indent) : ctools_export_object($table, $object, $indent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around ctools_export_crud_load() for < 1.7 compatibility.
|
||||
*/
|
||||
function _ctools_features_export_crud_load($table, $name) {
|
||||
if (ctools_api_version('1.7')) {
|
||||
return ctools_export_crud_load($table, $name);
|
||||
}
|
||||
elseif ($objects = ctools_export_load_object($table, 'names', array($name))) {
|
||||
return array_shift($objects);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around ctools_export_default_list() for < 1.7 compatibility.
|
||||
*/
|
||||
function _ctools_features_export_default_list($table, $schema) {
|
||||
if (ctools_api_version('1.7')) {
|
||||
return ctools_export_default_list($table, $schema);
|
||||
}
|
||||
elseif ($objects = ctools_export_load_object($table, 'all')) {
|
||||
return drupal_map_assoc(array_keys($objects));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around ctools_export_crud_delete() for < 1.7 compatibility.
|
||||
*/
|
||||
function _ctools_features_export_crud_delete($table, $object) {
|
||||
if (ctools_api_version('1.7')) {
|
||||
ctools_export_crud_delete($table, $object);
|
||||
}
|
||||
else {
|
||||
$schema = ctools_export_get_schema($table);
|
||||
$export = $schema['export'];
|
||||
db_query("DELETE FROM {{$table}} WHERE {$export['key']} = '%s'", $object->{$export['key']});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render() for page_manager.
|
||||
*/
|
||||
function page_manager_pages_features_export_render($module, $data) {
|
||||
// Reset the export display static to prevent clashes.
|
||||
drupal_static_reset('panels_export_display');
|
||||
|
||||
// Ensure that handlers have their code included before exporting.
|
||||
page_manager_get_tasks();
|
||||
return ctools_component_features_export_render('page_manager_pages', $module, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert() for page_manager.
|
||||
*/
|
||||
function page_manager_pages_features_revert($module) {
|
||||
if ($pages = features_get_default('page_manager_pages', $module)) {
|
||||
require_once drupal_get_path('module', 'ctools') . '/page_manager/plugins/tasks/page.inc';
|
||||
foreach ($pages as $page) {
|
||||
page_manager_page_delete($page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_pipe_COMPONENT_alter() for views_view.
|
||||
*/
|
||||
function views_features_pipe_views_view_alter(&$pipe, $data, $export) {
|
||||
// @todo Remove this check before next stable release.
|
||||
if (!function_exists('views_plugin_list')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$map = array_flip($data);
|
||||
foreach (views_plugin_list() as $plugin) {
|
||||
foreach ($plugin['views'] as $view_name) {
|
||||
if (isset($map[$view_name])) {
|
||||
$pipe['dependencies'][$plugin['module']] = $plugin['module'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function features_features_api() {
|
||||
return array(
|
||||
'dependencies' => array(
|
||||
'name' => 'Dependencies',
|
||||
'feature_source' => TRUE,
|
||||
'duplicates' => FEATURES_DUPLICATES_ALLOWED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function dependencies_features_export_options() {
|
||||
// Excluded modules.
|
||||
$excluded = drupal_required_modules();
|
||||
$options = array();
|
||||
foreach (features_get_modules() as $module_name => $info) {
|
||||
if (!in_array($module_name, $excluded) && $info->status && !empty($info->info)) {
|
||||
$options[$module_name] = $info->info['name'];
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function dependencies_features_export($data, &$export, $module_name = '') {
|
||||
// Don't allow a module to depend upon itself.
|
||||
if (!empty($data[$module_name])) {
|
||||
unset($data[$module_name]);
|
||||
}
|
||||
|
||||
// Clean up existing dependencies and merge.
|
||||
$export['dependencies'] = _features_export_minimize_dependencies($export['dependencies'], $module_name);
|
||||
$export['dependencies'] = array_merge($data, $export['dependencies']);
|
||||
$export['dependencies'] = array_unique($export['dependencies']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function dependencies_features_revert($module) {
|
||||
dependencies_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
* Ensure that all of a feature's dependencies are enabled.
|
||||
*/
|
||||
function dependencies_features_rebuild($module) {
|
||||
$feature = features_get_features($module);
|
||||
if (!empty($feature->info['dependencies'])) {
|
||||
$install = array();
|
||||
foreach ($feature->info['dependencies'] as $dependency) {
|
||||
// Parse the dependency string into the module name and version information.
|
||||
$parsed_dependency = drupal_parse_dependency($dependency);
|
||||
$dependency = $parsed_dependency['name'];
|
||||
if (!module_exists($dependency)) {
|
||||
$install[] = $dependency;
|
||||
}
|
||||
}
|
||||
if (!empty($install)) {
|
||||
features_install_modules($install);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,530 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function field_features_api() {
|
||||
return array(
|
||||
'field' => array(
|
||||
// this is deprecated by field_base and field_instance
|
||||
// but retained for compatibility with older exports
|
||||
'name' => t('Fields'),
|
||||
'default_hook' => 'field_default_fields',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => FALSE,
|
||||
),
|
||||
'field_base' => array(
|
||||
'name' => t('Field Bases'),
|
||||
'default_hook' => 'field_default_field_bases',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => TRUE,
|
||||
'supersedes' => 'field',
|
||||
),
|
||||
'field_instance' => array(
|
||||
'name' => t('Field Instances'),
|
||||
'default_hook' => 'field_default_field_instances',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => TRUE,
|
||||
'supersedes' => 'field',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function field_base_features_export_options() {
|
||||
$options = array();
|
||||
$fields = field_info_fields();
|
||||
foreach ($fields as $field_name => $field) {
|
||||
$options[$field_name] = $field_name;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function field_instance_features_export_options() {
|
||||
$options = array();
|
||||
foreach (field_info_fields() as $field_name => $field) {
|
||||
foreach ($field['bundles'] as $entity_type => $bundles) {
|
||||
foreach ($bundles as $bundle) {
|
||||
$identifier = "{$entity_type}-{$bundle}-{$field_name}";
|
||||
$options[$identifier] = $identifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($options);
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function field_base_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array();
|
||||
$map = features_get_default_map('field_base');
|
||||
|
||||
// The field_default_field_bases() hook integration is provided by the
|
||||
// features module so we need to add it as a dependency.
|
||||
$export['dependencies']['features'] = 'features';
|
||||
|
||||
foreach ($data as $identifier) {
|
||||
if ($base = features_field_base_load($identifier)) {
|
||||
// If this field 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']['field_base'][$identifier])) {
|
||||
unset($export['features']['field_base'][$identifier]);
|
||||
}
|
||||
$module = $map[$identifier];
|
||||
$export['dependencies'][$module] = $module;
|
||||
}
|
||||
// If the field has not yet been exported, add it
|
||||
else {
|
||||
$export['features']['field_base'][$identifier] = $identifier;
|
||||
$export['dependencies'][$base['module']] = $base['module'];
|
||||
if ($base['storage']['type'] != variable_get('field_storage_default', 'field_sql_storage')) {
|
||||
$export['dependencies'][$base['storage']['module']] = $base['storage']['module'];
|
||||
}
|
||||
// If taxonomy field, add in the vocabulary
|
||||
if ($base['type'] == 'taxonomy_term_reference' && !empty($base['settings']['allowed_values'])) {
|
||||
foreach ($base['settings']['allowed_values'] as $allowed_values) {
|
||||
if (!empty($allowed_values['vocabulary'])) {
|
||||
$pipe['taxonomy'][] = $allowed_values['vocabulary'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function field_instance_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array('field_base' => array());
|
||||
$map = features_get_default_map('field_instance');
|
||||
|
||||
// The field_default_field_instances() hook integration is provided by the
|
||||
// features module so we need to add it as a dependency.
|
||||
$export['dependencies']['features'] = 'features';
|
||||
|
||||
foreach ($data as $identifier) {
|
||||
if ($instance = features_field_instance_load($identifier)) {
|
||||
// If this field 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']['field_instance'][$identifier])) {
|
||||
unset($export['features']['field_instance'][$identifier]);
|
||||
}
|
||||
$module = $map[$identifier];
|
||||
$export['dependencies'][$module] = $module;
|
||||
}
|
||||
// If the field has not yet been exported, add it
|
||||
else {
|
||||
$export['features']['field_instance'][$identifier] = $identifier;
|
||||
$export['dependencies'][$instance['widget']['module']] = $instance['widget']['module'];
|
||||
foreach ($instance['display'] as $key => $display) {
|
||||
if (isset($display['module'])) {
|
||||
$export['dependencies'][$display['module']] = $display['module'];
|
||||
// @TODO: handle the pipe to image styles
|
||||
}
|
||||
}
|
||||
$pipe['field_base'][] = $instance['field_name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function field_base_features_export_render($module, $data, $export = NULL) {
|
||||
$translatables = $code = array();
|
||||
$code[] = ' $field_bases = array();';
|
||||
$code[] = '';
|
||||
foreach ($data as $identifier) {
|
||||
if ($field = features_field_base_load($identifier)) {
|
||||
unset($field['columns']);
|
||||
// unset($field['locked']);
|
||||
// Only remove the 'storage' declaration if the field is using the default
|
||||
// storage type.
|
||||
if ($field['storage']['type'] == variable_get('field_storage_default', 'field_sql_storage')) {
|
||||
unset($field['storage']);
|
||||
}
|
||||
// If we still have a storage declaration here it means that a non-default
|
||||
// storage type was altered into to the field definition. And noone would
|
||||
// never need to change the 'details' key, so don't render it.
|
||||
if (isset($field['storage']['details'])) {
|
||||
unset($field['storage']['details']);
|
||||
}
|
||||
|
||||
_field_instance_features_export_sort($field);
|
||||
$field_export = features_var_export($field, ' ');
|
||||
$field_identifier = features_var_export($identifier);
|
||||
$code[] = " // Exported field_base: {$field_identifier}";
|
||||
$code[] = " \$field_bases[{$field_identifier}] = {$field_export};";
|
||||
$code[] = "";
|
||||
}
|
||||
}
|
||||
$code[] = ' return $field_bases;';
|
||||
$code = implode("\n", $code);
|
||||
return array('field_default_field_bases' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function field_instance_features_export_render($module, $data, $export = NULL) {
|
||||
$translatables = $code = array();
|
||||
|
||||
$code[] = ' $field_instances = array();';
|
||||
$code[] = '';
|
||||
|
||||
foreach ($data as $identifier) {
|
||||
if ($instance = features_field_instance_load($identifier)) {
|
||||
_field_instance_features_export_sort($instance);
|
||||
$field_export = features_var_export($instance, ' ');
|
||||
$instance_identifier = features_var_export($identifier);
|
||||
$code[] = " // Exported field_instance: {$instance_identifier}";
|
||||
$code[] = " \$field_instances[{$instance_identifier}] = {$field_export};";
|
||||
$code[] = "";
|
||||
|
||||
if (!empty($instance['label'])) {
|
||||
$translatables[] = $instance['label'];
|
||||
}
|
||||
if (!empty($instance['description'])) {
|
||||
$translatables[] = $instance['description'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($translatables)) {
|
||||
$code[] = features_translatables_export($translatables, ' ');
|
||||
}
|
||||
$code[] = ' return $field_instances;';
|
||||
$code = implode("\n", $code);
|
||||
return array('field_default_field_instances' => $code);
|
||||
}
|
||||
|
||||
// Helper to enforce consistency in field export arrays.
|
||||
function _field_instance_features_export_sort(&$field, $sort = TRUE) {
|
||||
|
||||
// Some arrays are not sorted to preserve order (for example allowed_values).
|
||||
static $sort_blacklist = array(
|
||||
'allowed_values',
|
||||
'format_handlers',
|
||||
);
|
||||
|
||||
if ($sort) {
|
||||
uksort($field, 'strnatcmp');
|
||||
}
|
||||
foreach ($field as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
_field_instance_features_export_sort($field[$k], !in_array($k, $sort_blacklist));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function field_base_features_revert($module) {
|
||||
field_base_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function field_instance_features_revert($module) {
|
||||
field_instance_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements of hook_features_rebuild().
|
||||
* Rebuilds fields from code defaults.
|
||||
*/
|
||||
function field_base_features_rebuild($module) {
|
||||
if ($fields = features_get_default('field_base', $module)) {
|
||||
field_info_cache_clear();
|
||||
|
||||
// Load all the existing field bases up-front so that we don't
|
||||
// have to rebuild the cache all the time.
|
||||
$existing_fields = field_info_fields();
|
||||
|
||||
foreach ($fields as $field) {
|
||||
// Create or update field.
|
||||
if (isset($existing_fields[$field['field_name']])) {
|
||||
$existing_field = $existing_fields[$field['field_name']];
|
||||
if ($field + $existing_field !== $existing_field) {
|
||||
field_update_field($field);
|
||||
}
|
||||
}
|
||||
else {
|
||||
field_create_field($field);
|
||||
$existing_fields[$field['field_name']] = $field;
|
||||
}
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements of hook_features_rebuild().
|
||||
* Rebuilds field instances from code defaults.
|
||||
*/
|
||||
function field_instance_features_rebuild($module) {
|
||||
if ($instances = features_get_default('field_instance', $module)) {
|
||||
field_info_cache_clear();
|
||||
|
||||
// Load all the existing instances up-front so that we don't
|
||||
// have to rebuild the cache all the time.
|
||||
$existing_instances = field_info_instances();
|
||||
|
||||
foreach ($instances as $field_instance) {
|
||||
// If the field base information does not exist yet, cancel out.
|
||||
if (!field_info_field($field_instance['field_name'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create or update field instance.
|
||||
if (isset($existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']])) {
|
||||
$existing_instance = $existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']];
|
||||
if ($field_instance + $existing_instance !== $existing_instance) {
|
||||
try {
|
||||
field_update_instance($field_instance);
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
watchdog('features', 'Attempt to update field instance %label (in %entity entity type %bundle bundle) failed: %message', array('%label' => $field_instance['field_name'], '%entity' => $field_instance['entity_type'], '%bundle' => $field_instance['bundle'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
field_create_instance($field_instance);
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
watchdog('features', 'Attempt to create field instance %label (in %entity entity type %bundle bundle) failed: %message', array('%label' => $field_instance['field_name'], '%entity' => $field_instance['entity_type'], '%bundle' => $field_instance['bundle'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
|
||||
}
|
||||
$existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']] = $field_instance;
|
||||
}
|
||||
}
|
||||
|
||||
if ($instances) {
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a field base configuration by a field_name identifier.
|
||||
*/
|
||||
function features_field_base_load($field_name) {
|
||||
if ($field_info = field_info_field($field_name)) {
|
||||
unset($field_info['id']);
|
||||
unset($field_info['bundles']);
|
||||
return $field_info;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a field's instance configuration by an entity_type-bundle-field_name
|
||||
* identifier.
|
||||
*/
|
||||
function features_field_instance_load($identifier) {
|
||||
list($entity_type, $bundle, $field_name) = explode('-', $identifier);
|
||||
if ($instance_info = field_info_instance($entity_type, $field_name, $bundle)) {
|
||||
unset($instance_info['id']);
|
||||
unset($instance_info['field_id']);
|
||||
return $instance_info;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ----- DEPRECATED FIELD EXPORT -----
|
||||
* keep this code for backward compatibility with older exports
|
||||
* until v3.x
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function field_features_export_options() {
|
||||
$options = array();
|
||||
$instances = field_info_instances();
|
||||
foreach ($instances as $entity_type => $bundles) {
|
||||
foreach ($bundles as $bundle => $fields) {
|
||||
foreach ($fields as $field) {
|
||||
$identifier = "{$entity_type}-{$bundle}-{$field['field_name']}";
|
||||
$options[$identifier] = $identifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function field_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array();
|
||||
// Convert 'field' to 'field_instance' on features-update.
|
||||
$pipe['field_instance'] = $data;
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function field_features_export_render($module, $data, $export = NULL) {
|
||||
$translatables = $code = array();
|
||||
|
||||
$code[] = ' $fields = array();';
|
||||
$code[] = '';
|
||||
foreach ($data as $identifier) {
|
||||
if ($field = features_field_load($identifier)) {
|
||||
unset($field['field_config']['columns']);
|
||||
// Only remove the 'storage' declaration if the field is using the default
|
||||
// storage type.
|
||||
if ($field['field_config']['storage']['type'] == variable_get('field_storage_default', 'field_sql_storage')) {
|
||||
unset($field['field_config']['storage']);
|
||||
}
|
||||
// If we still have a storage declaration here it means that a non-default
|
||||
// storage type was altered into to the field definition. And noone would
|
||||
// never need to change the 'details' key, so don't render it.
|
||||
if (isset($field['field_config']['storage']['details'])) {
|
||||
unset($field['field_config']['storage']['details']);
|
||||
}
|
||||
|
||||
_field_features_export_sort($field);
|
||||
$field_export = features_var_export($field, ' ');
|
||||
$field_identifier = features_var_export($identifier);
|
||||
$code[] = " // Exported field: {$field_identifier}.";
|
||||
$code[] = " \$fields[{$field_identifier}] = {$field_export};";
|
||||
$code[] = "";
|
||||
|
||||
// Add label and description to translatables array.
|
||||
if (!empty($field['field_instance']['label'])) {
|
||||
$translatables[] = $field['field_instance']['label'];
|
||||
}
|
||||
if (!empty($field['field_instance']['description'])) {
|
||||
$translatables[] = $field['field_instance']['description'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($translatables)) {
|
||||
$code[] = features_translatables_export($translatables, ' ');
|
||||
}
|
||||
$code[] = ' return $fields;';
|
||||
$code = implode("\n", $code);
|
||||
return array('field_default_fields' => $code);
|
||||
}
|
||||
|
||||
// Helper to enforce consistency in field export arrays.
|
||||
function _field_features_export_sort(&$field, $sort = TRUE) {
|
||||
|
||||
// Some arrays are not sorted to preserve order (for example allowed_values).
|
||||
static $sort_blacklist = array(
|
||||
'allowed_values',
|
||||
'format_handlers',
|
||||
);
|
||||
|
||||
if ($sort) {
|
||||
ksort($field);
|
||||
}
|
||||
foreach ($field as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
_field_features_export_sort($field[$k], !in_array($k, $sort_blacklist));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function field_features_revert($module) {
|
||||
field_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements of hook_features_rebuild().
|
||||
* Rebuilds fields from code defaults.
|
||||
*/
|
||||
function field_features_rebuild($module) {
|
||||
if ($fields = features_get_default('field', $module)) {
|
||||
field_info_cache_clear();
|
||||
|
||||
// Load all the existing fields and instance up-front so that we don't
|
||||
// have to rebuild the cache all the time.
|
||||
$existing_fields = field_info_fields();
|
||||
$existing_instances = field_info_instances();
|
||||
|
||||
foreach ($fields as $field) {
|
||||
// Create or update field.
|
||||
$field_config = $field['field_config'];
|
||||
if (isset($existing_fields[$field_config['field_name']])) {
|
||||
$existing_field = $existing_fields[$field_config['field_name']];
|
||||
if ($field_config + $existing_field !== $existing_field) {
|
||||
try {
|
||||
field_update_field($field_config);
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
watchdog('features', 'Attempt to update field %label failed: %message', array('%label' => $field_config['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
field_create_field($field_config);
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
watchdog('features', 'Attempt to create field %label failed: %message', array('%label' => $field_config['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
|
||||
}
|
||||
$existing_fields[$field_config['field_name']] = $field_config;
|
||||
}
|
||||
|
||||
// Create or update field instance.
|
||||
$field_instance = $field['field_instance'];
|
||||
if (isset($existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']])) {
|
||||
$existing_instance = $existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']];
|
||||
if ($field_instance + $existing_instance !== $existing_instance) {
|
||||
field_update_instance($field_instance);
|
||||
}
|
||||
}
|
||||
else {
|
||||
field_create_instance($field_instance);
|
||||
$existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']] = $field_instance;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fields) {
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a field's configuration and instance configuration by an
|
||||
* entity_type-bundle-field_name identifier.
|
||||
*/
|
||||
function features_field_load($identifier) {
|
||||
list($entity_type, $bundle, $field_name) = explode('-', $identifier);
|
||||
$field_info = field_info_field($field_name);
|
||||
$instance_info = field_info_instance($entity_type, $field_name, $bundle);
|
||||
if ($field_info && $instance_info) {
|
||||
unset($field_info['id']);
|
||||
unset($field_info['bundles']);
|
||||
unset($instance_info['id']);
|
||||
unset($instance_info['field_id']);
|
||||
return array(
|
||||
'field_config' => $field_info,
|
||||
'field_instance' => $instance_info,
|
||||
);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function filter_features_api() {
|
||||
return array(
|
||||
'filter' => array(
|
||||
'name' => t('Text formats'),
|
||||
'default_hook' => 'filter_default_formats',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => TRUE
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function filter_features_export_options() {
|
||||
$options = array();
|
||||
foreach (filter_formats() as $format => $info) {
|
||||
$options[$format] = $info->name;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function filter_features_export($data, &$export, $module_name = '') {
|
||||
// The filter_default_formats() hook integration is provided by the
|
||||
// features module so we need to add it as a dependency.
|
||||
$export['dependencies']['features'] = 'features';
|
||||
|
||||
$filter_info = filter_get_filters();
|
||||
foreach ($data as $name) {
|
||||
if ($format = features_filter_format_load($name)) {
|
||||
// Add format to exports
|
||||
$export['features']['filter'][$format->format] = $format->format;
|
||||
|
||||
// Iterate through filters and ensure each filter's module is included as a dependency
|
||||
foreach (array_keys($format->filters) as $name) {
|
||||
if (isset($filter_info[$name], $filter_info[$name]['module'])) {
|
||||
$module = $filter_info[$name]['module'];
|
||||
$export['dependencies'][$module] = $module;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pipe = array();
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function filter_features_export_render($module, $data, $export = NULL) {
|
||||
$code = array();
|
||||
$code[] = ' $formats = array();';
|
||||
$code[] = '';
|
||||
|
||||
foreach ($data as $name) {
|
||||
if ($format = features_filter_format_load($name)) {
|
||||
$format_export = features_var_export($format, ' ');
|
||||
$format_identifier = features_var_export($format->format);
|
||||
$code[] = " // Exported format: {$format->name}.";
|
||||
$code[] = " \$formats[{$format_identifier}] = {$format_export};";
|
||||
$code[] = "";
|
||||
}
|
||||
}
|
||||
|
||||
$code[] = ' return $formats;';
|
||||
$code = implode("\n", $code);
|
||||
return array('filter_default_formats' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function filter_features_revert($module) {
|
||||
return filter_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
*/
|
||||
function filter_features_rebuild($module) {
|
||||
if ($defaults = features_get_default('filter', $module)) {
|
||||
foreach ($defaults as $format) {
|
||||
$format = (object) $format;
|
||||
filter_format_save($format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a filter format by its name.
|
||||
*/
|
||||
function features_filter_format_load($name) {
|
||||
// Use machine name for retrieving the format if available.
|
||||
$query = db_select('filter_format');
|
||||
$query->fields('filter_format');
|
||||
$query->condition('format', $name);
|
||||
|
||||
// Retrieve filters for the format and attach.
|
||||
if ($format = $query->execute()->fetchObject()) {
|
||||
$format->filters = array();
|
||||
foreach (filter_list_format($format->format) as $filter) {
|
||||
if (!empty($filter->status)) {
|
||||
$format->filters[$filter->name]['weight'] = $filter->weight;
|
||||
$format->filters[$filter->name]['status'] = $filter->status;
|
||||
$format->filters[$filter->name]['settings'] = $filter->settings;
|
||||
}
|
||||
}
|
||||
return $format;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function image_features_api() {
|
||||
return array(
|
||||
'image' => array(
|
||||
'name' => t('Image styles'),
|
||||
'feature_source' => TRUE,
|
||||
'default_hook' => 'image_default_styles',
|
||||
'alter_hook' => 'image_styles',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function image_features_export_options() {
|
||||
$options = array();
|
||||
foreach (image_styles() as $name => $style) {
|
||||
$options[$name] = $style['name'];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function image_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array();
|
||||
$map = features_get_default_map('image');
|
||||
foreach ($data as $style) {
|
||||
$export['dependencies']['image'] = 'image';
|
||||
// If another module provides this style, add it as a dependency
|
||||
if (isset($map[$style]) && $map[$style] != $module_name) {
|
||||
$module = $map[$style];
|
||||
$export['dependencies'][$module] = $module;
|
||||
}
|
||||
// Otherwise, export the style
|
||||
elseif (image_style_load($style)) {
|
||||
$export['features']['image'][$style] = $style;
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function image_features_export_render($module_name, $data, $export = NULL) {
|
||||
$code = array();
|
||||
$code[] = ' $styles = array();';
|
||||
$code[] = '';
|
||||
foreach ($data as $name) {
|
||||
if ($style = image_style_load($name)) {
|
||||
_image_features_style_sanitize($style);
|
||||
$style_export = features_var_export($style, ' ');
|
||||
$style_identifier = features_var_export($name);
|
||||
$code[] = " // Exported image style: {$name}.";
|
||||
$code[] = " \$styles[{$style_identifier}] = {$style_export};";
|
||||
$code[] = "";
|
||||
}
|
||||
}
|
||||
$code[] = ' return $styles;';
|
||||
$code = implode("\n", $code);
|
||||
return array('image_default_styles' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function image_features_revert($module) {
|
||||
if ($default_styles = features_get_default('image', $module)) {
|
||||
foreach (array_keys($default_styles) as $default_style) {
|
||||
if ($style = image_style_load($default_style)) {
|
||||
if ($style['storage'] != IMAGE_STORAGE_DEFAULT) {
|
||||
image_default_style_revert($style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecessary keys for export.
|
||||
*/
|
||||
function _image_features_style_sanitize(&$style, $child = FALSE) {
|
||||
$omit = $child ? array('isid', 'ieid', 'storage') : array('isid', 'ieid', 'storage', 'module');
|
||||
if (is_array($style)) {
|
||||
foreach ($style as $k => $v) {
|
||||
if (in_array($k, $omit, TRUE)) {
|
||||
unset($style[$k]);
|
||||
}
|
||||
else if (is_array($v)) {
|
||||
_image_features_style_sanitize($style[$k], TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Features hooks for language.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements of hook_features_api().
|
||||
*/
|
||||
function locale_features_api() {
|
||||
return array(
|
||||
'language' => array(
|
||||
'name' => t('Languages'),
|
||||
'default_hook' => 'locale_default_languages',
|
||||
'feature_source' => TRUE,
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function language_features_export_options() {
|
||||
return locale_language_list('native', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function language_features_export($data, &$export, $module_name = '') {
|
||||
$export['dependencies']['features'] = 'features';
|
||||
$export['dependencies']['locale'] = 'locale';
|
||||
|
||||
$language_list = locale_language_list('native', TRUE);
|
||||
|
||||
foreach ($data as $name) {
|
||||
// Only export existing languages.
|
||||
if (!empty($language_list[$name])) {
|
||||
// Add language to exports.
|
||||
$export['features']['language'][$name] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
// No pipe to return.
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function language_features_export_render($module, $data, $export = NULL) {
|
||||
$code = array();
|
||||
$code[] = ' $languages = array();';
|
||||
$code[] = '';
|
||||
|
||||
$language_list = language_list();
|
||||
|
||||
foreach ($data as $name) {
|
||||
// Only render existing languages.
|
||||
if (!empty($language_list[$name])) {
|
||||
|
||||
$var = (array) $language_list[$name];
|
||||
// Unset javascript hash
|
||||
unset($var['javascript']);
|
||||
|
||||
$lang_export = features_var_export($var, ' ');
|
||||
$lang_identifier = features_var_export($name);
|
||||
$code[] = " // Exported language: $name.";
|
||||
$code[] = " \$languages[{$lang_identifier}] = {$lang_export};";
|
||||
}
|
||||
}
|
||||
|
||||
$code[] = ' return $languages;';
|
||||
$code = implode("\n", $code);
|
||||
return array('locale_default_languages' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function language_features_revert($module) {
|
||||
return language_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
*/
|
||||
function language_features_rebuild($module) {
|
||||
if ($defaults = features_get_default('language', $module)) {
|
||||
foreach ($defaults as $key => $language) {
|
||||
_features_language_save((object) $language);
|
||||
}
|
||||
|
||||
// Set correct language count.
|
||||
$enabled_languages = db_select('languages')
|
||||
->condition('enabled', 1)
|
||||
->fields('languages')
|
||||
->execute()
|
||||
->rowCount();
|
||||
variable_set('language_count', $enabled_languages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to save the language to database.
|
||||
*
|
||||
* @see locale_languages_edit_form_submit()
|
||||
*/
|
||||
function _features_language_save($language) {
|
||||
|
||||
$current_language = db_select('languages')
|
||||
->condition('language', $language->language)
|
||||
->fields('languages')
|
||||
->execute()
|
||||
->fetchAssoc();
|
||||
|
||||
// Set the default language when needed.
|
||||
$default = language_default();
|
||||
|
||||
// Insert new language via api function.
|
||||
if (empty($current_language)) {
|
||||
locale_add_language($language->language,
|
||||
$language->name,
|
||||
$language->native,
|
||||
$language->direction,
|
||||
$language->domain,
|
||||
$language->prefix,
|
||||
$language->enabled,
|
||||
($language->language == $default->language));
|
||||
// Additional params, locale_add_language does not implement.
|
||||
db_update('languages')
|
||||
->fields(array(
|
||||
'plurals' => empty($language->plurals) ? 0 : $language->plurals,
|
||||
'formula' => empty($language->formula) ? '' : $language->formula,
|
||||
))
|
||||
->condition('language', $language->language)
|
||||
->execute();
|
||||
}
|
||||
// Update Existing language.
|
||||
else {
|
||||
// @TODO: get properties from schema.
|
||||
$properties = array('language', 'name', 'native', 'direction', 'enabled', 'plurals', 'formula', 'domain', 'prefix', 'weight', 'javascript');
|
||||
// The javascript hash is not in the imported data but should be empty
|
||||
if (!isset($language->javascript)) {
|
||||
$language->javascript = '';
|
||||
}
|
||||
|
||||
$fields = array_intersect_key((array) $language, array_flip($properties));
|
||||
db_update('languages')
|
||||
->fields($fields)
|
||||
->condition('language', $language->language)
|
||||
->execute();
|
||||
|
||||
// Set the default language when needed.
|
||||
$default = language_default();
|
||||
if ($default->language == $language->language) {
|
||||
variable_set('language_default', (object) $fields);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,414 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function menu_features_api() {
|
||||
return array(
|
||||
'menu_custom' => array(
|
||||
'name' => t('Menus'),
|
||||
'default_hook' => 'menu_default_menu_custom',
|
||||
'feature_source' => TRUE,
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
),
|
||||
'menu_links' => array(
|
||||
'name' => t('Menu links'),
|
||||
'default_hook' => 'menu_default_menu_links',
|
||||
'feature_source' => TRUE,
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
),
|
||||
// DEPRECATED
|
||||
'menu' => array(
|
||||
'name' => t('Menu items'),
|
||||
'default_hook' => 'menu_default_items',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
'feature_source' => FALSE,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
* DEPRECATED: This implementation simply migrates deprecated `menu` items
|
||||
* to the `menu_links` type.
|
||||
*/
|
||||
function menu_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array();
|
||||
foreach ($data as $path) {
|
||||
$pipe['menu_links'][] = "features:{$path}";
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function menu_custom_features_export_options() {
|
||||
$options = array();
|
||||
$result = db_query("SELECT * FROM {menu_custom} ORDER BY title", array(), array('fetch' => PDO::FETCH_ASSOC));
|
||||
foreach ($result as $menu) {
|
||||
$options[$menu['menu_name']] = $menu['title'];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function menu_custom_features_export($data, &$export, $module_name = '') {
|
||||
// Default hooks are provided by the feature module so we need to add
|
||||
// it as a dependency.
|
||||
$export['dependencies']['features'] = 'features';
|
||||
$export['dependencies']['menu'] = 'menu';
|
||||
|
||||
// Collect a menu to module map
|
||||
$pipe = array();
|
||||
$map = features_get_default_map('menu_custom', 'menu_name');
|
||||
foreach ($data as $menu_name) {
|
||||
// If this menu is provided by a different module, add it as a dependency.
|
||||
if (isset($map[$menu_name]) && $map[$menu_name] != $module_name) {
|
||||
$export['dependencies'][$map[$menu_name]] = $map[$menu_name];
|
||||
}
|
||||
else {
|
||||
$export['features']['menu_custom'][$menu_name] = $menu_name;
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render()
|
||||
*/
|
||||
function menu_custom_features_export_render($module, $data) {
|
||||
$code = array();
|
||||
$code[] = ' $menus = array();';
|
||||
$code[] = '';
|
||||
|
||||
$translatables = array();
|
||||
foreach ($data as $menu_name) {
|
||||
$row = db_select('menu_custom')
|
||||
->fields('menu_custom')
|
||||
->condition('menu_name', $menu_name)
|
||||
->execute()
|
||||
->fetchAssoc();
|
||||
if ($row) {
|
||||
$export = features_var_export($row, ' ');
|
||||
$code[] = " // Exported menu: {$menu_name}.";
|
||||
$code[] = " \$menus['{$menu_name}'] = {$export};";
|
||||
$translatables[] = $row['title'];
|
||||
$translatables[] = $row['description'];
|
||||
}
|
||||
}
|
||||
if (!empty($translatables)) {
|
||||
$code[] = features_translatables_export($translatables, ' ');
|
||||
}
|
||||
|
||||
$code[] = '';
|
||||
$code[] = ' return $menus;';
|
||||
$code = implode("\n", $code);
|
||||
return array('menu_default_menu_custom' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function menu_custom_features_revert($module) {
|
||||
menu_custom_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
*/
|
||||
function menu_custom_features_rebuild($module) {
|
||||
if ($defaults = features_get_default('menu_custom', $module)) {
|
||||
foreach ($defaults as $menu) {
|
||||
menu_save($menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function menu_links_features_export_options() {
|
||||
global $menu_admin;
|
||||
// Need to set this to TRUE in order to get menu links that the
|
||||
// current user may not have access to (i.e. user/login)
|
||||
$menu_admin = TRUE;
|
||||
$use_menus = array_intersect_key(menu_get_menus(), array_flip(array_filter(variable_get('features_admin_menu_links_menus', array_keys(menu_get_menus())))));
|
||||
$menu_links = menu_parent_options($use_menus, array('mlid' => 0));
|
||||
$options = array();
|
||||
foreach ($menu_links as $key => $name) {
|
||||
list($menu_name, $mlid) = explode(':', $key, 2);
|
||||
if ($mlid != 0) {
|
||||
$link = menu_link_load($mlid);
|
||||
$identifier = menu_links_features_identifier($link, TRUE);
|
||||
$options[$identifier] = "{$menu_name}: {$name}";
|
||||
}
|
||||
}
|
||||
$menu_admin = FALSE;
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for generating the menu link exportable identifier.
|
||||
*/
|
||||
function menu_links_features_identifier($link, $old = FALSE) {
|
||||
// Add some uniqueness to these identifiers, allowing multiple links with the same path, but different titles.
|
||||
$clean_title = features_clean_title(isset($link['title']) ? $link['title'] : $link['link_title']);
|
||||
|
||||
// The old identifier is requested.
|
||||
if ($old) {
|
||||
// if identifier already exists
|
||||
if (isset($link['options']['identifier'])) {
|
||||
return $link['options']['identifier'];
|
||||
}
|
||||
// providing backward compatibility and allowing/enabling multiple links with same paths
|
||||
else {
|
||||
$identifier = isset($link['menu_name'], $link['link_path']) ? "{$link['menu_name']}:{$link['link_path']}" : FALSE;
|
||||
// Checking if there are multiples of this identifier
|
||||
if (features_menu_link_load($identifier) !== FALSE) {
|
||||
// this is where we return the upgrade posibility for links.
|
||||
return $identifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isset($link['menu_name'], $link['link_path']) ? "{$link['menu_name']}_{$clean_title}:{$link['link_path']}" : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function menu_links_features_export($data, &$export, $module_name = '') {
|
||||
// Default hooks are provided by the feature module so we need to add
|
||||
// it as a dependency.
|
||||
$export['dependencies']['features'] = 'features';
|
||||
$export['dependencies']['menu'] = 'menu';
|
||||
|
||||
// Collect a link to module map
|
||||
$pipe = array();
|
||||
$map = features_get_default_map('menu_links', 'menu_links_features_identifier');
|
||||
foreach ($data as $key => $identifier) {
|
||||
if ($link = features_menu_link_load($identifier)) {
|
||||
// If this link is provided by a different module, add it as a dependency.
|
||||
$new_identifier = menu_links_features_identifier($link, empty($export));
|
||||
if (isset($map[$identifier]) && $map[$identifier] != $module_name) {
|
||||
$export['dependencies'][$map[$identifier]] = $map[$identifier];
|
||||
}
|
||||
else {
|
||||
$export['features']['menu_links'][$new_identifier] = $new_identifier;
|
||||
}
|
||||
// For now, exclude a variety of common menus from automatic export.
|
||||
// They may still be explicitly included in a Feature if the builder
|
||||
// chooses to do so.
|
||||
if (!in_array($link['menu_name'], array('features', 'primary-links', 'secondary-links', 'navigation', 'admin', 'devel'))) {
|
||||
$pipe['menu_custom'][] = $link['menu_name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render()
|
||||
*/
|
||||
function menu_links_features_export_render($module, $data, $export = NULL) {
|
||||
$code = array();
|
||||
$code[] = ' $menu_links = array();';
|
||||
$code[] = '';
|
||||
|
||||
$translatables = array();
|
||||
foreach ($data as $identifier) {
|
||||
|
||||
if ($link = features_menu_link_load($identifier)) {
|
||||
$new_identifier = menu_links_features_identifier($link, empty($export));
|
||||
|
||||
// Replace plid with a parent path.
|
||||
if (!empty($link['plid']) && $parent = menu_link_load($link['plid'])) {
|
||||
// If the new identifier is different than the old, maintain
|
||||
// 'parent_path' for backwards compatibility.
|
||||
if ($new_identifier != menu_links_features_identifier($link)) {
|
||||
$link['parent_path'] = $parent['link_path'];
|
||||
}
|
||||
else {
|
||||
$clean_title = features_clean_title($parent['title']);
|
||||
$link['parent_identifier'] = "{$parent['menu_name']}_{$clean_title}:{$parent['link_path']}";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($export)) {
|
||||
// Don't show new identifier unless we are actually exporting.
|
||||
$link['options']['identifier'] = $new_identifier;
|
||||
// identifiers are renewed, => that means we need to update them in the db
|
||||
menu_link_save($temp = $link);
|
||||
}
|
||||
|
||||
unset($link['plid']);
|
||||
unset($link['mlid']);
|
||||
|
||||
$code[] = " // Exported menu link: {$new_identifier}";
|
||||
$code[] = " \$menu_links['{$new_identifier}'] = ". features_var_export($link, ' ') .";";
|
||||
$translatables[] = $link['link_title'];
|
||||
}
|
||||
}
|
||||
if (!empty($translatables)) {
|
||||
$code[] = features_translatables_export($translatables, ' ');
|
||||
}
|
||||
|
||||
$code[] = '';
|
||||
$code[] = ' return $menu_links;';
|
||||
$code = implode("\n", $code);
|
||||
return array('menu_default_menu_links' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function menu_links_features_revert($module) {
|
||||
menu_links_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
*/
|
||||
function menu_links_features_rebuild($module) {
|
||||
if ($menu_links = features_get_default('menu_links', $module)) {
|
||||
menu_links_features_rebuild_ordered($menu_links);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a depth tree of all menu links.
|
||||
*/
|
||||
function menu_links_features_rebuild_ordered($menu_links, $reset = FALSE) {
|
||||
static $ordered;
|
||||
static $all_links;
|
||||
if (!isset($ordered) || $reset) {
|
||||
$ordered = array();
|
||||
$unordered = features_get_default('menu_links');
|
||||
|
||||
// Order all links by depth.
|
||||
if ($unordered) {
|
||||
do {
|
||||
$current = count($unordered);
|
||||
foreach ($unordered as $key => $link) {
|
||||
$identifier = menu_links_features_identifier($link);
|
||||
$parent = isset($link['parent_identifier']) ? $link['parent_identifier'] : '';
|
||||
if (empty($parent)) {
|
||||
$ordered[$identifier] = 0;
|
||||
$all_links[$identifier] = $link;
|
||||
unset($unordered[$key]);
|
||||
}
|
||||
elseif (isset($ordered[$parent])) {
|
||||
$ordered[$identifier] = $ordered[$parent] + 1;
|
||||
$all_links[$identifier] = $link;
|
||||
unset($unordered[$key]);
|
||||
}
|
||||
}
|
||||
} while (count($unordered) < $current);
|
||||
}
|
||||
asort($ordered);
|
||||
}
|
||||
|
||||
// Ensure any default menu items that do not exist are created.
|
||||
foreach (array_keys($ordered) as $identifier) {
|
||||
$link = $all_links[$identifier];
|
||||
|
||||
$existing = features_menu_link_load($identifier);
|
||||
if (!$existing || in_array($link, $menu_links)) {
|
||||
// Retrieve the mlid if this is an existing item.
|
||||
if ($existing) {
|
||||
$link['mlid'] = $existing['mlid'];
|
||||
}
|
||||
// Retrieve the plid for a parent link.
|
||||
if (!empty($link['parent_identifier']) && $parent = features_menu_link_load($link['parent_identifier'])) {
|
||||
$link['plid'] = $parent['mlid'];
|
||||
}
|
||||
// This if for backwards compatibility.
|
||||
elseif (!empty($link['parent_path']) && $parent = features_menu_link_load("{$link['menu_name']}:{$link['parent_path']}")) {
|
||||
$link['plid'] = $parent['mlid'];
|
||||
}
|
||||
else {
|
||||
$link['plid'] = 0;
|
||||
}
|
||||
menu_link_save($link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a menu link by its menu_name_cleantitle:link_path identifier.
|
||||
* Also matches links with unique menu_name:link_path
|
||||
*/
|
||||
function features_menu_link_load($identifier) {
|
||||
$menu_name = '';
|
||||
$link_path = '';
|
||||
// This gets variables for menu_name_cleantitle:link_path format.
|
||||
if (strstr($identifier, "_")) {
|
||||
$link_path = substr($identifier, strpos($identifier, ":") + 1);
|
||||
list($menu_name) = explode('_', $identifier, 2);
|
||||
$clean_title = substr($identifier, strpos($identifier, "_") + 1, strpos($identifier, ":") - strpos($identifier, "_") - 1);
|
||||
}
|
||||
// This gets variables for traditional identifier format.
|
||||
else {
|
||||
$clean_title = '';
|
||||
list($menu_name, $link_path) = explode(':', $identifier, 2);
|
||||
}
|
||||
$links = db_select('menu_links')
|
||||
->fields('menu_links', array('menu_name', 'mlid', 'plid', 'link_path', 'router_path', 'link_title', 'options', 'module', 'hidden', 'external', 'has_children', 'expanded', 'weight', 'customized'))
|
||||
->condition('menu_name', $menu_name)
|
||||
->condition('link_path', $link_path)
|
||||
->execute()
|
||||
->fetchAllAssoc('mlid');
|
||||
|
||||
foreach($links as $link) {
|
||||
$link->options = unserialize($link->options);
|
||||
|
||||
// Title or previous identifier matches.
|
||||
if ((isset($link->options['identifier']) && strcmp($link->options['identifier'], $identifier) == 0)
|
||||
|| (isset($clean_title) && strcmp(features_clean_title($link->link_title), $clean_title) == 0)) {
|
||||
|
||||
return (array)$link;
|
||||
}
|
||||
}
|
||||
|
||||
// Only one link with the requested menu_name and link_path does exists,
|
||||
// -- providing an upgrade possibility for links saved in a feature before the
|
||||
// new identifier-pattern was added.
|
||||
if (count($links) == 1 && empty($clean_title)) {
|
||||
$link = reset($links); // get the first item
|
||||
return (array)$link;
|
||||
}
|
||||
// If link_path was changed on an existing link, we need to find it by
|
||||
// searching for link_title.
|
||||
else if (isset($clean_title)) {
|
||||
$links = db_select('menu_links')
|
||||
->fields('menu_links', array('menu_name', 'mlid', 'plid', 'link_path', 'router_path', 'link_title', 'options', 'module', 'hidden', 'external', 'has_children', 'expanded', 'weight'))
|
||||
->condition('menu_name', $menu_name)
|
||||
->execute()
|
||||
->fetchAllAssoc('mlid');
|
||||
|
||||
foreach($links as $link) {
|
||||
$link->options = unserialize($link->options);
|
||||
|
||||
// title or previous identifier matches
|
||||
if ((isset($link->options['identifier']) && strcmp($link->options['identifier'], $identifier) == 0)
|
||||
|| (isset($clean_title) && strcmp(features_clean_title($link->link_title), $clean_title) == 0)) {
|
||||
|
||||
return (array)$link;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a lowercase clean string with only letters, numbers and dashes
|
||||
*/
|
||||
function features_clean_title($str) {
|
||||
return strtolower(preg_replace_callback('/(\s)|([^a-zA-Z\-0-9])/i', create_function(
|
||||
'$matches',
|
||||
'return $matches[1]?"-":"";'
|
||||
), $str));
|
||||
}
|
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function node_features_api() {
|
||||
return array(
|
||||
'node' => array(
|
||||
'name' => t('Content types'),
|
||||
'feature_source' => TRUE,
|
||||
'default_hook' => 'node_info',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function node_features_export_options() {
|
||||
return node_type_get_names();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export.
|
||||
*/
|
||||
function node_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array();
|
||||
$map = features_get_default_map('node');
|
||||
|
||||
foreach ($data as $type) {
|
||||
// Poll node module to determine who provides the node type.
|
||||
if ($info = node_type_get_type($type)) {
|
||||
// If this node type is provided by a different module, add it as a dependency
|
||||
if (isset($map[$type]) && $map[$type] != $module_name) {
|
||||
$export['dependencies'][$map[$type]] = $map[$type];
|
||||
}
|
||||
// Otherwise export the node type.
|
||||
elseif (in_array($info->base, array('node_content', 'features'))) {
|
||||
$export['features']['node'][$type] = $type;
|
||||
$export['dependencies']['node'] = 'node';
|
||||
$export['dependencies']['features'] = 'features';
|
||||
}
|
||||
|
||||
$fields = field_info_instances('node', $type);
|
||||
foreach ($fields as $name => $field) {
|
||||
$pipe['field_instance'][] = "node-{$field['bundle']}-{$field['field_name']}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function node_features_export_render($module, $data, $export = NULL) {
|
||||
$elements = array(
|
||||
'name' => TRUE,
|
||||
'base' => FALSE,
|
||||
'description' => TRUE,
|
||||
'has_title' => FALSE,
|
||||
'title_label' => TRUE,
|
||||
'help' => TRUE,
|
||||
);
|
||||
$output = array();
|
||||
$output[] = ' $items = array(';
|
||||
foreach ($data as $type) {
|
||||
if ($info = node_type_get_type($type)) {
|
||||
// Force module name to be 'features' if set to 'node. If we leave as
|
||||
// 'node' the content type will be assumed to be database-stored by
|
||||
// the node module.
|
||||
$info->base = ($info->base === 'node') ? 'features' : $info->base;
|
||||
$output[] = " '{$type}' => array(";
|
||||
foreach ($elements as $key => $t) {
|
||||
if ($t) {
|
||||
$text = str_replace("'", "\'", $info->$key);
|
||||
$text = !empty($text) ? "t('{$text}')" : "''";
|
||||
$output[] = " '{$key}' => {$text},";
|
||||
}
|
||||
else {
|
||||
$output[] = " '{$key}' => '{$info->$key}',";
|
||||
}
|
||||
}
|
||||
$output[] = " ),";
|
||||
}
|
||||
}
|
||||
$output[] = ' );';
|
||||
$output[] = ' return $items;';
|
||||
$output = implode("\n", $output);
|
||||
return array('node_info' => $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*
|
||||
* @param $module
|
||||
* name of module to revert content for
|
||||
*/
|
||||
function node_features_revert($module = NULL) {
|
||||
if ($default_types = features_get_default('node', $module)) {
|
||||
foreach ($default_types as $type_name => $type_info) {
|
||||
// Delete node types
|
||||
// We don't use node_type_delete() because we do not actually
|
||||
// want to delete the node type (and invoke hook_node_type()).
|
||||
// This can lead to bad consequences like CCK deleting field
|
||||
// storage in the DB.
|
||||
db_delete('node_type')
|
||||
->condition('type', $type_name)
|
||||
->execute();
|
||||
}
|
||||
node_types_rebuild();
|
||||
menu_rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_disable().
|
||||
*
|
||||
* When a features module is disabled, modify any node types it provides so
|
||||
* they can be deleted manually through the content types UI.
|
||||
*
|
||||
* @param $module
|
||||
* Name of module that has been disabled.
|
||||
*/
|
||||
function node_features_disable($module) {
|
||||
if ($default_types = features_get_default('node', $module)) {
|
||||
foreach ($default_types as $type_name => $type_info) {
|
||||
$type_info = node_type_load($type_name);
|
||||
$type_info->module = 'node';
|
||||
$type_info->custom = 1;
|
||||
$type_info->modified = 1;
|
||||
$type_info->locked = 0;
|
||||
node_type_save($type_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_enable().
|
||||
*
|
||||
* When a features module is enabled, modify any node types it provides so
|
||||
* they can no longer be deleted manually through the content types UI.
|
||||
*
|
||||
* @param $module
|
||||
* Name of module that has been enabled.
|
||||
*/
|
||||
function node_features_enable($module) {
|
||||
if ($default_types = features_get_default('node', $module)) {
|
||||
foreach ($default_types as $type_name => $type_info) {
|
||||
// Ensure the type exists.
|
||||
if ($type_info = node_type_load($type_name)) {
|
||||
$type_info->module = $module;
|
||||
$type_info->custom = 0;
|
||||
$type_info->modified = 0;
|
||||
$type_info->locked = 1;
|
||||
node_type_save($type_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function taxonomy_features_api() {
|
||||
return array(
|
||||
'taxonomy' => array(
|
||||
'name' => t('Taxonomy'),
|
||||
'feature_source' => TRUE,
|
||||
'default_hook' => 'taxonomy_default_vocabularies',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function taxonomy_features_export_options() {
|
||||
$vocabularies = array();
|
||||
foreach (taxonomy_get_vocabularies() as $vocabulary) {
|
||||
$vocabularies[$vocabulary->machine_name] = $vocabulary->name;
|
||||
}
|
||||
return $vocabularies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*
|
||||
* @todo Test adding existing dependencies.
|
||||
*/
|
||||
function taxonomy_features_export($data, &$export, $module_name = '') {
|
||||
$pipe = array();
|
||||
|
||||
// taxonomy_default_vocabularies integration is provided by Features.
|
||||
$export['dependencies']['features'] = 'features';
|
||||
$export['dependencies']['taxonomy'] = 'taxonomy';
|
||||
|
||||
// Add dependencies for each vocabulary.
|
||||
$map = features_get_default_map('taxonomy');
|
||||
foreach ($data as $machine_name) {
|
||||
if (isset($map[$machine_name]) && $map[$machine_name] != $module_name) {
|
||||
$export['dependencies'][$map[$machine_name]] = $map[$machine_name];
|
||||
}
|
||||
else {
|
||||
$export['features']['taxonomy'][$machine_name] = $machine_name;
|
||||
|
||||
$fields = field_info_instances('taxonomy_term', $machine_name);
|
||||
foreach ($fields as $name => $field) {
|
||||
$pipe['field'][] = "taxonomy_term-{$field['bundle']}-{$field['field_name']}";
|
||||
$pipe['field_instance'][] = "taxonomy_term-{$field['bundle']}-{$field['field_name']}";
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function taxonomy_features_export_render($module, $data) {
|
||||
$vocabularies = taxonomy_get_vocabularies();
|
||||
$code = array();
|
||||
foreach ($data as $machine_name) {
|
||||
foreach ($vocabularies as $vocabulary) {
|
||||
if ($vocabulary->machine_name == $machine_name) {
|
||||
// We don't want to break the entity cache, so we need to clone the
|
||||
// vocabulary before unsetting the id.
|
||||
$vocabulary = clone $vocabulary;
|
||||
unset($vocabulary->vid);
|
||||
$code[$machine_name] = $vocabulary;
|
||||
}
|
||||
}
|
||||
}
|
||||
$code = " return ". features_var_export($code, ' ') .";";
|
||||
return array('taxonomy_default_vocabularies' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function taxonomy_features_revert($module) {
|
||||
taxonomy_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
*
|
||||
* Rebuilds Taxonomy vocabularies from code defaults.
|
||||
*/
|
||||
function taxonomy_features_rebuild($module) {
|
||||
if ($vocabularies = features_get_default('taxonomy', $module)) {
|
||||
$existing = taxonomy_get_vocabularies();
|
||||
foreach ($vocabularies as $vocabulary) {
|
||||
$vocabulary = (object) $vocabulary;
|
||||
foreach ($existing as $existing_vocab) {
|
||||
if ($existing_vocab->machine_name === $vocabulary->machine_name) {
|
||||
$vocabulary->vid = $existing_vocab->vid;
|
||||
}
|
||||
}
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_features_api().
|
||||
*/
|
||||
function user_features_api() {
|
||||
return array(
|
||||
'user_role' => array(
|
||||
'name' => t('Roles'),
|
||||
'feature_source' => TRUE,
|
||||
'default_hook' => 'user_default_roles',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
),
|
||||
'user_permission' => array(
|
||||
'name' => t('Permissions'),
|
||||
'feature_source' => TRUE,
|
||||
'default_hook' => 'user_default_permissions',
|
||||
'default_file' => FEATURES_DEFAULTS_INCLUDED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function user_permission_features_export($data, &$export, $module_name = '') {
|
||||
$export['dependencies']['features'] = 'features';
|
||||
|
||||
// Ensure the modules that provide the given permissions are included as dependencies.
|
||||
$map = user_permission_get_modules();
|
||||
foreach ($data as $perm) {
|
||||
$perm_name = $perm;
|
||||
// Export vocabulary permissions using the machine name, instead of
|
||||
// vocabulary id.
|
||||
_user_features_change_term_permission($perm_name, 'machine_name');
|
||||
if (isset($map[$perm_name])) {
|
||||
$perm_module = $map[$perm_name];
|
||||
$export['dependencies'][$perm_module] = $perm_module;
|
||||
$export['features']['user_permission'][$perm] = $perm;
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function user_permission_features_export_options() {
|
||||
$modules = array();
|
||||
$module_info = system_get_info('module');
|
||||
foreach (module_implements('permission') as $module) {
|
||||
$modules[$module_info[$module]['name']] = $module;
|
||||
}
|
||||
ksort($modules);
|
||||
|
||||
$options = array();
|
||||
foreach ($modules as $display_name => $module) {
|
||||
if ($permissions = module_invoke($module, 'permission')) {
|
||||
foreach ($permissions as $perm => $perm_item) {
|
||||
// Export vocabulary permissions using the machine name, instead of
|
||||
// vocabulary id.
|
||||
_user_features_change_term_permission($perm);
|
||||
$options[$perm] = strip_tags("{$display_name}: {$perm_item['title']}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function user_permission_features_export_render($module, $data) {
|
||||
$perm_modules = &drupal_static(__FUNCTION__ . '_perm_modules');
|
||||
if (!isset($perm_modules)) {
|
||||
$perm_modules = user_permission_get_modules();
|
||||
}
|
||||
|
||||
$code = array();
|
||||
$code[] = ' $permissions = array();';
|
||||
$code[] = '';
|
||||
|
||||
$permissions = _user_features_get_permissions();
|
||||
|
||||
foreach ($data as $perm_name) {
|
||||
$permission = array();
|
||||
// Export vocabulary permissions using the machine name, instead of
|
||||
// vocabulary id.
|
||||
$perm = $perm_name;
|
||||
_user_features_change_term_permission($perm_name, 'machine_name');
|
||||
$permission['name'] = $perm;
|
||||
if (!empty($permissions[$perm_name])) {
|
||||
sort($permissions[$perm_name]);
|
||||
$permission['roles'] = drupal_map_assoc($permissions[$perm_name]);
|
||||
}
|
||||
else {
|
||||
$permission['roles'] = array();
|
||||
}
|
||||
if (isset($perm_modules[$perm_name])) {
|
||||
$permission['module'] = $perm_modules[$perm_name];
|
||||
}
|
||||
$perm_identifier = features_var_export($perm);
|
||||
$perm_export = features_var_export($permission, ' ');
|
||||
$code[] = " // Exported permission: {$perm_identifier}.";
|
||||
$code[] = " \$permissions[{$perm_identifier}] = {$perm_export};";
|
||||
$code[] = "";
|
||||
}
|
||||
|
||||
$code[] = ' return $permissions;';
|
||||
$code = implode("\n", $code);
|
||||
return array('user_default_permissions' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function user_permission_features_revert($module) {
|
||||
user_permission_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
* Iterate through default permissions and update the permissions map.
|
||||
*
|
||||
* @param $module
|
||||
* The module whose default user permissions should be rebuilt.
|
||||
*/
|
||||
function user_permission_features_rebuild($module) {
|
||||
if ($defaults = features_get_default('user_permission', $module)) {
|
||||
// Make sure the list of available node types is up to date, especially when
|
||||
// installing multiple features at once, for example from an install profile
|
||||
// or via drush.
|
||||
node_types_rebuild();
|
||||
|
||||
$modules = user_permission_get_modules();
|
||||
$roles = _user_features_get_roles();
|
||||
$permissions_by_role = _user_features_get_permissions(FALSE);
|
||||
foreach ($defaults as $permission) {
|
||||
$perm = $permission['name'];
|
||||
_user_features_change_term_permission($perm, 'machine_name');
|
||||
if (empty($modules[$perm])) {
|
||||
$args = array('!name' => $perm, '!module' => $module,);
|
||||
$msg = t('Warning in features rebuild of !module. No module defines permission "!name".', $args);
|
||||
drupal_set_message($msg, 'warning');
|
||||
continue;
|
||||
}
|
||||
// Export vocabulary permissions using the machine name, instead of
|
||||
// vocabulary id.
|
||||
foreach ($roles as $role) {
|
||||
if (in_array($role, $permission['roles'])) {
|
||||
$permissions_by_role[$role][$perm] = TRUE;
|
||||
}
|
||||
else {
|
||||
$permissions_by_role[$role][$perm] = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Write the updated permissions.
|
||||
foreach ($roles as $rid => $role) {
|
||||
if (isset($permissions_by_role[$role])) {
|
||||
user_role_change_permissions($rid, $permissions_by_role[$role]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export().
|
||||
*/
|
||||
function user_role_features_export($data, &$export, $module_name = '') {
|
||||
$export['dependencies']['features'] = 'features';
|
||||
$map = features_get_default_map('user_role', 'name');
|
||||
foreach ($data as $role) {
|
||||
// Role is provided by another module. Add dependency.
|
||||
if (isset($map[$role]) && $map[$role] != $module_name) {
|
||||
$export['dependencies'][$map[$role]] = $map[$role];
|
||||
}
|
||||
// Export.
|
||||
elseif(user_role_load_by_name($role)) {
|
||||
$export['features']['user_role'][$role] = $role;
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_options().
|
||||
*/
|
||||
function user_role_features_export_options() {
|
||||
return drupal_map_assoc(_user_features_get_roles(FALSE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_render().
|
||||
*/
|
||||
function user_role_features_export_render($module, $data) {
|
||||
$code = array();
|
||||
$code[] = ' $roles = array();';
|
||||
$code[] = '';
|
||||
|
||||
foreach ($data as $name) {
|
||||
if ($role = user_role_load_by_name($name)) {
|
||||
unset($role->rid);
|
||||
$role_identifier = features_var_export($name);
|
||||
$role_export = features_var_export($role , ' ');
|
||||
$code[] = " // Exported role: {$name}.";
|
||||
$code[] = " \$roles[{$role_identifier}] = {$role_export};";
|
||||
$code[] = "";
|
||||
}
|
||||
}
|
||||
|
||||
$code[] = ' return $roles;';
|
||||
$code = implode("\n", $code);
|
||||
return array('user_default_roles' => $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_revert().
|
||||
*/
|
||||
function user_role_features_revert($module) {
|
||||
user_role_features_rebuild($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_rebuild().
|
||||
*/
|
||||
function user_role_features_rebuild($module) {
|
||||
if ($defaults = features_get_default('user_role', $module)) {
|
||||
foreach ($defaults as $role) {
|
||||
$role = (object) $role;
|
||||
if ($existing = user_role_load_by_name($role->name)) {
|
||||
$role->rid = $existing->rid;
|
||||
}
|
||||
user_role_save($role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate $rid => $role with role names untranslated.
|
||||
*/
|
||||
function _user_features_get_roles($builtin = TRUE) {
|
||||
$roles = array();
|
||||
foreach (user_roles() as $rid => $name) {
|
||||
switch ($rid) {
|
||||
case DRUPAL_ANONYMOUS_RID:
|
||||
if ($builtin) {
|
||||
$roles[$rid] = 'anonymous user';
|
||||
}
|
||||
break;
|
||||
case DRUPAL_AUTHENTICATED_RID:
|
||||
if ($builtin) {
|
||||
$roles[$rid] = 'authenticated user';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$roles[$rid] = $name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent the current state of permissions as a perm to role name array map.
|
||||
*/
|
||||
function _user_features_get_permissions($by_role = TRUE) {
|
||||
$map = user_permission_get_modules();
|
||||
$roles = _user_features_get_roles();
|
||||
$permissions = array();
|
||||
foreach (user_role_permissions($roles) as $rid => $role_permissions) {
|
||||
if ($by_role) {
|
||||
foreach (array_keys(array_filter($role_permissions)) as $permission) {
|
||||
if (isset($map[$permission])) {
|
||||
$permissions[$permission][] = $roles[$rid];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$permissions[$roles[$rid]] = array();
|
||||
foreach ($role_permissions as $permission => $status) {
|
||||
if (isset($map[$permission])) {
|
||||
$permissions[$roles[$rid]][$permission] = $status;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $permissions;
|
||||
}
|
289
sites/all/modules/contrib/admin/features/tests/features.test
Normal file
289
sites/all/modules/contrib/admin/features/tests/features.test
Normal file
@@ -0,0 +1,289 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* User permission component tests for Features
|
||||
*/
|
||||
class FeaturesUserTestCase extends DrupalWebTestCase {
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Test info.
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => t('Component tests'),
|
||||
'description' => t('Run tests for components of Features.') ,
|
||||
'group' => t('Features'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up test.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp(array(
|
||||
'field',
|
||||
'filter',
|
||||
'image',
|
||||
'taxonomy',
|
||||
'views',
|
||||
'features',
|
||||
'features_test'
|
||||
));
|
||||
|
||||
// Run a features rebuild to ensure our feature is fully installed.
|
||||
features_rebuild();
|
||||
|
||||
$admin_user = $this->drupalCreateUser(array('administer features'));
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run test.
|
||||
*/
|
||||
public function test() {
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
|
||||
$components = array_filter(array(
|
||||
'field_instance' => 'field',
|
||||
'filter' => 'filter',
|
||||
'image' => 'image',
|
||||
'node' => 'node',
|
||||
'user_permission' => 'user',
|
||||
'views_view' => 'views',
|
||||
), 'module_exists');
|
||||
|
||||
foreach (array_keys($components) as $component) {
|
||||
$callback = "_test_{$component}";
|
||||
|
||||
// Ensure that the component/default is properly available.
|
||||
$object = $this->$callback('load');
|
||||
$this->assertTrue(!empty($object), t('@component present.', array('@component' => $component)));
|
||||
|
||||
// Ensure that the component is defaulted.
|
||||
$states = features_get_component_states(array('features_test'), FALSE, TRUE);
|
||||
$this->assertTrue($states['features_test'][$component] === FEATURES_DEFAULT, t('@component state: Default.', array('@component' => $component)));
|
||||
|
||||
// Override component and test that Features detects the override.
|
||||
$this->$callback('override', $this);
|
||||
$states = features_get_component_states(array('features_test'), FALSE, TRUE);
|
||||
$this->assertTrue($states['features_test'][$component] === FEATURES_OVERRIDDEN, t('@component state: Overridden.', array('@component' => $component)));
|
||||
}
|
||||
|
||||
// Revert component and ensure that component has reverted.
|
||||
// Do this in separate loops so we only have to run
|
||||
// drupal_flush_all_caches() once.
|
||||
foreach (array_keys($components) as $component) {
|
||||
features_revert(array('features_test' => array($component)));
|
||||
}
|
||||
drupal_flush_all_caches();
|
||||
foreach (array_keys($components) as $component) {
|
||||
// Reload so things like Views can clear it's cache
|
||||
$this->$callback('load');
|
||||
$states = features_get_component_states(array('features_test'), FALSE, TRUE);
|
||||
$this->assertTrue($states['features_test'][$component] === FEATURES_DEFAULT, t('@component reverted.', array('@component' => $component)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function _test_field_instance($op = 'load') {
|
||||
switch ($op) {
|
||||
case 'load':
|
||||
return field_info_instance('node', 'field_features_test', 'features_test');
|
||||
case 'override':
|
||||
$field_instance = field_info_instance('node', 'field_features_test', 'features_test');
|
||||
$field_instance['label'] = 'Foo bar';
|
||||
field_update_instance($field_instance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _test_filter($op = 'load') {
|
||||
// So... relying on our own API functions to test is pretty lame.
|
||||
// But these modules don't have APIs either. So might as well use
|
||||
// the ones we've written for them...
|
||||
features_include();
|
||||
switch ($op) {
|
||||
case 'load':
|
||||
return features_filter_format_load('features_test');
|
||||
case 'override':
|
||||
$format = features_filter_format_load('features_test');
|
||||
unset($format->filters['filter_url']);
|
||||
filter_format_save($format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _test_image($op = 'load') {
|
||||
switch ($op) {
|
||||
case 'load':
|
||||
return image_style_load('features_test');
|
||||
case 'override':
|
||||
$style = image_style_load('features_test');
|
||||
$style = image_style_save($style);
|
||||
foreach ($style['effects'] as $effect) {
|
||||
$effect['data']['width'] = '120';
|
||||
image_effect_save($effect);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _test_node($op = 'load') {
|
||||
switch ($op) {
|
||||
case 'load':
|
||||
return node_type_get_type('features_test');
|
||||
case 'override':
|
||||
$type = node_type_get_type('features_test');
|
||||
$type->description = 'Foo bar baz.';
|
||||
$type->modified = TRUE;
|
||||
node_type_save($type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _test_views_view($op = 'load') {
|
||||
switch ($op) {
|
||||
case 'load':
|
||||
return views_get_view('features_test', TRUE);
|
||||
case 'override':
|
||||
$view = views_get_view('features_test', TRUE);
|
||||
$view->set_display('default');
|
||||
$view->display_handler->override_option('title', 'Foo bar');
|
||||
$view->save();
|
||||
// Clear the load cache from above
|
||||
views_get_view('features_test', TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _test_user_permission($op = 'load') {
|
||||
switch ($op) {
|
||||
case 'load':
|
||||
$permissions = user_role_permissions(array(DRUPAL_AUTHENTICATED_RID => 'authenticated user'));
|
||||
return !empty($permissions[DRUPAL_AUTHENTICATED_RID]['create features_test content']);
|
||||
case 'override':
|
||||
user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array('create features_test content' => 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests enabling of feature modules.
|
||||
*/
|
||||
class FeaturesEnableTestCase extends DrupalWebTestCase {
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Test info.
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => t('Features enable tests'),
|
||||
'description' => t('Run tests for enabling of features.') ,
|
||||
'group' => t('Features'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run test for features_get_components on enable.
|
||||
*/
|
||||
public function testFeaturesGetComponents() {
|
||||
|
||||
// Testing that features_get_components returns correct after enable.
|
||||
$modules = array(
|
||||
'features',
|
||||
'taxonomy',
|
||||
'features_test',
|
||||
);
|
||||
|
||||
// Make sure features_get_components is cached if features already enabled.
|
||||
if (!module_exists('features')) {
|
||||
drupal_load('module', 'features');
|
||||
}
|
||||
features_get_components();
|
||||
|
||||
module_enable($modules);
|
||||
|
||||
// Make sure correct information for enabled modules is now cached.
|
||||
$components = features_get_components();
|
||||
$taxonomy_component_info = taxonomy_features_api();
|
||||
$this->assertTrue(!empty($components['taxonomy']) && $components['taxonomy'] == $taxonomy_component_info['taxonomy'], 'features_get_components returns correct taxonomy information on enable');
|
||||
|
||||
features_rebuild();
|
||||
$this->assertNotNull(taxonomy_vocabulary_machine_name_load('taxonomy_features_test'), 'Taxonomy vocabulary correctly enabled on enable.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests intergration of ctools for features.
|
||||
*/
|
||||
class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Test info.
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => t('Features Chaos Tools integration'),
|
||||
'description' => t('Run tests for ctool integration of features.') ,
|
||||
'group' => t('Features'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up test.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp(array(
|
||||
'features',
|
||||
'ctools',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run test.
|
||||
*/
|
||||
public function testModuleEnable() {
|
||||
$try = array(
|
||||
'strongarm',
|
||||
'views',
|
||||
);
|
||||
|
||||
// Trigger the first includes and the static to be set.
|
||||
features_include();
|
||||
$function_ends = array(
|
||||
'features_export',
|
||||
'features_export_options',
|
||||
'features_export_render',
|
||||
'features_revert',
|
||||
);
|
||||
foreach ($try as $module) {
|
||||
$function = $module . '_features_api';
|
||||
$this->assertFalse(function_exists($function), 'Chaos tools functions for ' . $module . ' do not exist while it is disabled.');
|
||||
// Module enable will trigger declaring the new functions.
|
||||
module_enable(array($module));
|
||||
}
|
||||
|
||||
// CTools hooks only created when there is an actual feature exportable
|
||||
// enabled.
|
||||
module_enable(array('features_test'));
|
||||
|
||||
foreach ($try as $module) {
|
||||
if (module_exists($module)) {
|
||||
$function_exists = function_exists($function);
|
||||
if ($function_exists) {
|
||||
foreach ($function() as $component_type => $component_info) {
|
||||
foreach ($function_ends as $function_end) {
|
||||
$function_exists = $function_exists && function_exists($component_type . '_' . $function_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->assertTrue($function_exists, 'Chaos tools functions for ' . $module . ' exist when it is enabled.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* features_test.features.field_base.inc
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_field_default_field_bases().
|
||||
*/
|
||||
function features_test_field_default_field_bases() {
|
||||
$field_bases = array();
|
||||
|
||||
// Exported field_base: 'field_features_test'
|
||||
$field_bases['field_features_test'] = array(
|
||||
'active' => 1,
|
||||
'cardinality' => 1,
|
||||
'deleted' => 0,
|
||||
'entity_types' => array(),
|
||||
'field_name' => 'field_features_test',
|
||||
'foreign keys' => array(
|
||||
'format' => array(
|
||||
'columns' => array(
|
||||
'format' => 'format',
|
||||
),
|
||||
'table' => 'filter_format',
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'format' => array(
|
||||
0 => 'format',
|
||||
),
|
||||
),
|
||||
'locked' => 0,
|
||||
'module' => 'text',
|
||||
'settings' => array(
|
||||
'max_length' => 255,
|
||||
),
|
||||
'translatable' => 1,
|
||||
'type' => 'text',
|
||||
);
|
||||
|
||||
return $field_bases;
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* features_test.features.field_instance.inc
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_field_default_field_instances().
|
||||
*/
|
||||
function features_test_field_default_field_instances() {
|
||||
$field_instances = array();
|
||||
|
||||
// Exported field_instance: 'node-features_test-field_features_test'
|
||||
$field_instances['node-features_test-field_features_test'] = array(
|
||||
'bundle' => 'features_test',
|
||||
'default_value' => NULL,
|
||||
'deleted' => 0,
|
||||
'description' => '',
|
||||
'display' => array(
|
||||
'default' => array(
|
||||
'label' => 'above',
|
||||
'module' => 'text',
|
||||
'settings' => array(),
|
||||
'type' => 'text_default',
|
||||
'weight' => 0,
|
||||
),
|
||||
'full' => array(
|
||||
'label' => 'above',
|
||||
'settings' => array(),
|
||||
'type' => 'hidden',
|
||||
'weight' => 0,
|
||||
),
|
||||
'print' => array(
|
||||
'label' => 'above',
|
||||
'settings' => array(),
|
||||
'type' => 'hidden',
|
||||
'weight' => 0,
|
||||
),
|
||||
'rss' => array(
|
||||
'label' => 'above',
|
||||
'settings' => array(),
|
||||
'type' => 'hidden',
|
||||
'weight' => 0,
|
||||
),
|
||||
'teaser' => array(
|
||||
'label' => 'above',
|
||||
'settings' => array(),
|
||||
'type' => 'hidden',
|
||||
'weight' => 0,
|
||||
),
|
||||
),
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'field_features_test',
|
||||
'label' => 'Test',
|
||||
'required' => 0,
|
||||
'settings' => array(
|
||||
'text_processing' => 0,
|
||||
'user_register_form' => FALSE,
|
||||
),
|
||||
'widget' => array(
|
||||
'active' => 1,
|
||||
'module' => 'text',
|
||||
'settings' => array(
|
||||
'size' => 60,
|
||||
),
|
||||
'type' => 'text_textfield',
|
||||
'weight' => -4,
|
||||
),
|
||||
);
|
||||
|
||||
// Translatables
|
||||
// Included for use with string extractors like potx.
|
||||
t('Test');
|
||||
|
||||
return $field_instances;
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* features_test.features.filter.inc
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_filter_default_formats().
|
||||
*/
|
||||
function features_test_filter_default_formats() {
|
||||
$formats = array();
|
||||
|
||||
// Exported format: features_test.
|
||||
$formats['features_test'] = array(
|
||||
'format' => 'features_test',
|
||||
'name' => 'features_test',
|
||||
'cache' => 1,
|
||||
'status' => 1,
|
||||
'weight' => 0,
|
||||
'filters' => array(
|
||||
'filter_autop' => array(
|
||||
'weight' => 10,
|
||||
'status' => 1,
|
||||
'settings' => array(),
|
||||
),
|
||||
'filter_html' => array(
|
||||
'weight' => 10,
|
||||
'status' => 1,
|
||||
'settings' => array(
|
||||
'allowed_html' => '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>',
|
||||
'filter_html_help' => 1,
|
||||
'filter_html_nofollow' => 0,
|
||||
),
|
||||
),
|
||||
'filter_htmlcorrector' => array(
|
||||
'weight' => 10,
|
||||
'status' => 1,
|
||||
'settings' => array(),
|
||||
),
|
||||
'filter_html_escape' => array(
|
||||
'weight' => 10,
|
||||
'status' => 1,
|
||||
'settings' => array(),
|
||||
),
|
||||
'filter_url' => array(
|
||||
'weight' => 10,
|
||||
'status' => 1,
|
||||
'settings' => array(
|
||||
'filter_url_length' => 72,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $formats;
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* features_test.features.inc
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_ctools_plugin_api().
|
||||
*/
|
||||
function features_test_ctools_plugin_api() {
|
||||
list($module, $api) = func_get_args();
|
||||
if ($module == "strongarm" && $api == "strongarm") {
|
||||
return array("version" => "1");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_api().
|
||||
*/
|
||||
function features_test_views_api() {
|
||||
return array("api" => "3.0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_image_default_styles().
|
||||
*/
|
||||
function features_test_image_default_styles() {
|
||||
$styles = array();
|
||||
|
||||
// Exported image style: features_test.
|
||||
$styles['features_test'] = array(
|
||||
'name' => 'features_test',
|
||||
'effects' => array(
|
||||
2 => array(
|
||||
'label' => 'Scale',
|
||||
'help' => 'Scaling will maintain the aspect-ratio of the original image. If only a single dimension is specified, the other dimension will be calculated.',
|
||||
'effect callback' => 'image_scale_effect',
|
||||
'dimensions callback' => 'image_scale_dimensions',
|
||||
'form callback' => 'image_scale_form',
|
||||
'summary theme' => 'image_scale_summary',
|
||||
'module' => 'image',
|
||||
'name' => 'image_scale',
|
||||
'data' => array(
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'upscale' => 0,
|
||||
),
|
||||
'weight' => 1,
|
||||
),
|
||||
),
|
||||
'label' => 'features_test',
|
||||
);
|
||||
|
||||
return $styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_info().
|
||||
*/
|
||||
function features_test_node_info() {
|
||||
$items = array(
|
||||
'features_test' => array(
|
||||
'name' => t('Testing: Features'),
|
||||
'base' => 'node_content',
|
||||
'description' => t('Content type provided for Features tests.'),
|
||||
'has_title' => '1',
|
||||
'title_label' => t('Title'),
|
||||
'help' => '',
|
||||
),
|
||||
);
|
||||
return $items;
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* features_test.features.taxonomy.inc
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_taxonomy_default_vocabularies().
|
||||
*/
|
||||
function features_test_taxonomy_default_vocabularies() {
|
||||
return array(
|
||||
'taxonomy_features_test' => array(
|
||||
'name' => 'Taxonomy Features Test',
|
||||
'machine_name' => 'taxonomy_features_test',
|
||||
'description' => 'Taxonomy vocabulary',
|
||||
'hierarchy' => 0,
|
||||
'module' => 'taxonomy',
|
||||
'weight' => 0,
|
||||
'rdf_mapping' => array(
|
||||
'rdftype' => array(
|
||||
0 => 'skos:ConceptScheme',
|
||||
),
|
||||
'name' => array(
|
||||
'predicates' => array(
|
||||
0 => 'dc:title',
|
||||
),
|
||||
),
|
||||
'description' => array(
|
||||
'predicates' => array(
|
||||
0 => 'rdfs:comment',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* features_test.features.user_permission.inc
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_user_default_permissions().
|
||||
*/
|
||||
function features_test_user_default_permissions() {
|
||||
$permissions = array();
|
||||
|
||||
// Exported permission: create features_test content.
|
||||
$permissions['create features_test content'] = array(
|
||||
'name' => 'create features_test content',
|
||||
'roles' => array(
|
||||
'anonymous user' => 'anonymous user',
|
||||
'authenticated user' => 'authenticated user',
|
||||
),
|
||||
'module' => 'node',
|
||||
);
|
||||
|
||||
return $permissions;
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
name = Features Tests
|
||||
description = Test module for Features testing.
|
||||
core = 7.x
|
||||
package = Testing
|
||||
php = 5.2.0
|
||||
dependencies[] = features
|
||||
dependencies[] = image
|
||||
dependencies[] = strongarm
|
||||
dependencies[] = taxonomy
|
||||
dependencies[] = views
|
||||
features[ctools][] = strongarm:strongarm:1
|
||||
features[ctools][] = views:views_default:3.0
|
||||
features[features_api][] = api:2
|
||||
features[field_base][] = field_features_test
|
||||
features[field_instance][] = node-features_test-field_features_test
|
||||
features[filter][] = features_test
|
||||
features[image][] = features_test
|
||||
features[node][] = features_test
|
||||
features[taxonomy][] = taxonomy_features_test
|
||||
features[user_permission][] = create features_test content
|
||||
features[views_view][] = features_test
|
||||
hidden = 1
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-10-17
|
||||
version = "7.x-2.0+0-dev"
|
||||
core = "7.x"
|
||||
project = "features"
|
||||
datestamp = "1382036080"
|
||||
|
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
include_once('features_test.features.inc');
|
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* features_test.views_default.inc
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_default_views().
|
||||
*/
|
||||
function features_test_views_default_views() {
|
||||
$export = array();
|
||||
|
||||
$view = new view();
|
||||
$view->name = 'features_test';
|
||||
$view->description = 'Test view provided by Features testing module.';
|
||||
$view->tag = 'testing';
|
||||
$view->base_table = 'node';
|
||||
$view->human_name = '';
|
||||
$view->core = 0;
|
||||
$view->api_version = '3.0';
|
||||
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
|
||||
|
||||
/* Display: Defaults */
|
||||
$handler = $view->new_display('default', 'Defaults', 'default');
|
||||
$handler->display->display_options['title'] = 'Test';
|
||||
$handler->display->display_options['use_more_always'] = FALSE;
|
||||
$handler->display->display_options['access']['type'] = 'none';
|
||||
$handler->display->display_options['cache']['type'] = 'none';
|
||||
$handler->display->display_options['query']['type'] = 'views_query';
|
||||
$handler->display->display_options['query']['options']['query_comment'] = FALSE;
|
||||
$handler->display->display_options['exposed_form']['type'] = 'basic';
|
||||
$handler->display->display_options['pager']['type'] = 'full';
|
||||
$handler->display->display_options['style_plugin'] = 'default';
|
||||
$handler->display->display_options['row_plugin'] = 'node';
|
||||
$export['features_test'] = $view;
|
||||
|
||||
return $export;
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
?>
|
||||
<div class='clearfix features-components'>
|
||||
<div class='column'>
|
||||
<div class='info'>
|
||||
<h3><?php print $name ?></h3>
|
||||
<div class='description'><?php print $description ?></div>
|
||||
<?php print $dependencies ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='column'>
|
||||
<div class='components'>
|
||||
<?php print $components ?>
|
||||
<?php if (!empty($key)): ?>
|
||||
<div class='clearfix features-key'><?php print theme('links', array('links' => $key)) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($buttons)): ?>
|
||||
<div class='buttons clearfix'><?php print $buttons ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php print drupal_render_children($form) ?>
|
||||
</div>
|
331
sites/all/modules/contrib/admin/features/theme/theme.inc
Normal file
331
sites/all/modules/contrib/admin/features/theme/theme.inc
Normal file
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Display feature component info
|
||||
*/
|
||||
function template_preprocess_features_admin_components(&$vars) {
|
||||
drupal_add_css(drupal_get_path('module', 'features') . '/features.css');
|
||||
$form = $vars['form'];
|
||||
|
||||
// Basic info
|
||||
$vars['name'] = $form['#info']['name'];
|
||||
$vars['description'] = isset($form['#info']['description']) ? $form['#info']['description'] : '';
|
||||
|
||||
// Legend/key
|
||||
$vars['key'] = array();
|
||||
|
||||
// Dependencies
|
||||
$rows = array();
|
||||
$modules = features_get_info();
|
||||
foreach ($form['#dependencies'] as $dependency => $status) {
|
||||
$rows[] = array(
|
||||
array(
|
||||
'data' => isset($modules[$dependency]->info['name']) ? $modules[$dependency]->info['name'] : $dependency,
|
||||
'class' => 'component'
|
||||
),
|
||||
theme('features_module_status', array('status' => $status)),
|
||||
);
|
||||
}
|
||||
$vars['dependencies'] = theme('table', array('header' => array(t('Dependency'), t('Status')), 'rows' => $rows));
|
||||
|
||||
// Components
|
||||
$rows = array();
|
||||
$components = features_get_components();
|
||||
|
||||
// Display key for conflicting elements.
|
||||
if (!empty($form['#conflicts'])) {
|
||||
$vars['key'][] = array(
|
||||
'title' => theme('features_storage_link', array('storage' => FEATURES_CONFLICT, 'text' => t('Conflicts with another feature'))),
|
||||
'html' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($form['#info']['features'])) {
|
||||
foreach ($form['#info']['features'] as $component => $items) {
|
||||
if (!empty($items)) {
|
||||
$conflicts = array_key_exists($component, $form['#conflicts'])
|
||||
? $form['#conflicts'][$component]
|
||||
: NULL;
|
||||
|
||||
$header = $data = array();
|
||||
if (element_children($form['revert'])) {
|
||||
$header[] = array(
|
||||
'data' => isset($form['revert'][$component]) ? drupal_render($form['revert'][$component]) : '',
|
||||
'header' => TRUE
|
||||
);
|
||||
}
|
||||
$header[] = array(
|
||||
'data' => isset($components[$component]['name']) ? $components[$component]['name'] : $component,
|
||||
'header' => TRUE
|
||||
);
|
||||
$header[] = array(
|
||||
'data' => drupal_render($form['components'][$component]),
|
||||
'header' => TRUE
|
||||
);
|
||||
$rows[] = $header;
|
||||
|
||||
if (element_children($form['revert'])) {
|
||||
$data[] = '';
|
||||
}
|
||||
$data[] = array(
|
||||
'data' => theme('features_component_list', array('components' => $items, 'source' => $items, 'conflicts' => $conflicts)),
|
||||
'colspan' => 2,
|
||||
'class' => 'component'
|
||||
);
|
||||
$rows[] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
$vars['components'] = theme('table', array('header' => array(), 'rows' => $rows));
|
||||
|
||||
// Other elements
|
||||
$vars['buttons'] = drupal_render($form['buttons']);
|
||||
$vars['form'] = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Themes a module status display.
|
||||
*/
|
||||
function theme_features_module_status($vars) {
|
||||
switch ($vars['status']) {
|
||||
case FEATURES_MODULE_ENABLED:
|
||||
$text_status = t('Enabled');
|
||||
$class = 'admin-enabled';
|
||||
break;
|
||||
case FEATURES_MODULE_DISABLED:
|
||||
$text_status = t('Disabled');
|
||||
$class = 'admin-disabled';
|
||||
break;
|
||||
case FEATURES_MODULE_MISSING:
|
||||
$text_status = t('Missing');
|
||||
$class = 'admin-missing';
|
||||
break;
|
||||
case FEATURES_MODULE_CONFLICT:
|
||||
$text_status = t('Enabled');
|
||||
$class = 'admin-conflict';
|
||||
break;
|
||||
}
|
||||
$text = !empty($vars['module']) ? $vars['module'] . ' (' . $text_status . ')' : $text_status;
|
||||
return "<span class=\"$class\">$text</span>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Themes a module status display.
|
||||
*/
|
||||
function theme_features_storage_link($vars) {
|
||||
$classes = array(
|
||||
FEATURES_OVERRIDDEN => 'admin-overridden',
|
||||
FEATURES_DEFAULT => 'admin-default',
|
||||
FEATURES_NEEDS_REVIEW => 'admin-needs-review',
|
||||
FEATURES_REBUILDING => 'admin-rebuilding',
|
||||
FEATURES_REBUILDABLE => 'admin-rebuilding',
|
||||
FEATURES_CONFLICT => 'admin-conflict',
|
||||
FEATURES_DISABLED => 'admin-disabled',
|
||||
FEATURES_CHECKING => 'admin-loading',
|
||||
);
|
||||
$default_text = array(
|
||||
FEATURES_OVERRIDDEN => t('Overridden'),
|
||||
FEATURES_DEFAULT => t('Default'),
|
||||
FEATURES_NEEDS_REVIEW => t('Needs review'),
|
||||
FEATURES_REBUILDING => t('Rebuilding'),
|
||||
FEATURES_REBUILDABLE => t('Rebuilding'),
|
||||
FEATURES_CONFLICT => t('Conflict'),
|
||||
FEATURES_DISABLED => t('Disabled'),
|
||||
FEATURES_CHECKING => t('Checking...'),
|
||||
);
|
||||
$text = isset($vars['text']) ? $vars['text'] : $default_text[$vars['storage']];
|
||||
if ($vars['path']) {
|
||||
$vars['options']['attributes']['class'][] = $classes[$vars['storage']];
|
||||
$vars['options']['attributes']['class'][] = 'features-storage';
|
||||
return l($text, $vars['path'], $vars['options']);
|
||||
}
|
||||
else {
|
||||
return "<span class='{$classes[$vars['storage']]} features-storage'>{$text}</span>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme function for displaying form buttons
|
||||
*/
|
||||
function theme_features_form_buttons(&$vars) {
|
||||
drupal_add_css(drupal_get_path('module', 'features') . '/features.css');
|
||||
|
||||
$output = drupal_render_children($vars['element']);
|
||||
return !empty($output) ? "<div class='buttons clearfix'>{$output}</div>" : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme for features management form.
|
||||
*/
|
||||
function theme_features_form_package(&$vars) {
|
||||
drupal_add_css(drupal_get_path('module', 'features') . '/features.css');
|
||||
drupal_add_js(drupal_get_path('module', 'features') . '/features.js');
|
||||
|
||||
$output = '';
|
||||
|
||||
$header = array('', t('Feature'), t('Signature'));
|
||||
if (isset($vars['form']['state'])) {
|
||||
$header[] = t('State');
|
||||
}
|
||||
if (isset($vars['form']['actions'])) {
|
||||
$header[] = t('Actions');
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
foreach (element_children($vars['form']['status']) as $element) {
|
||||
// Yank title & description fields off the form element for
|
||||
// rendering in their own cells.
|
||||
$name = "<div class='feature'>";
|
||||
$name .= "<strong>{$vars['form']['status'][$element]['#title']}</strong>";
|
||||
$name .= "<div class='description'>{$vars['form']['status'][$element]['#description']}</div>";
|
||||
$name .= "</div>";
|
||||
unset($vars['form']['status'][$element]['#title']);
|
||||
unset($vars['form']['status'][$element]['#description']);
|
||||
|
||||
|
||||
// Determine row & cell classes
|
||||
$class = $vars['form']['status'][$element]['#default_value'] ? 'enabled' : 'disabled';
|
||||
|
||||
$row = array();
|
||||
$row['status'] = array('data' => drupal_render($vars['form']['status'][$element]), 'class' => array('status'));
|
||||
$row['name'] = array('data' => $name, 'class' => 'name');
|
||||
$row['sign'] = array('data' => drupal_render($vars['form']['sign'][$element]), 'class' => array('sign'));
|
||||
|
||||
if (isset($vars['form']['state'])) {
|
||||
$row['state'] = array('data' => drupal_render($vars['form']['state'][$element]), 'class' => array('state'));
|
||||
}
|
||||
if (isset($vars['form']['actions'])) {
|
||||
$row['actions'] = array('data' => drupal_render($vars['form']['actions'][$element]), 'class' => array('actions'));
|
||||
}
|
||||
$rows[] = array('data' => $row, 'class' => array($class));
|
||||
}
|
||||
|
||||
if (empty($rows)) {
|
||||
$rows[] = array('', array('data' => t('No features available.'), 'colspan' => count($header)));
|
||||
}
|
||||
|
||||
$class = count($header) > 3 ? 'features features-admin' : 'features features-manage';
|
||||
$output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'features-form-table', 'class' => array($class))));
|
||||
|
||||
// Prevent section from being rendered by drupal_render().
|
||||
|
||||
$output .= drupal_render($vars['form']['buttons']);
|
||||
$output .= drupal_render_children($vars['form']);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme functions ====================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Export selection / display for features export form.
|
||||
*/
|
||||
function theme_features_form_export(&$vars) {
|
||||
drupal_add_css(drupal_get_path('module', 'features') . '/features.css');
|
||||
drupal_add_js(drupal_get_path('module', 'features') . '/features.js');
|
||||
|
||||
$output = '';
|
||||
$output .= "<div class='clearfix features-components'>";
|
||||
$output .= "<div class='column'>" . drupal_render($vars['form']['components']) . drupal_render($vars['form']['sources']) . "</div>";
|
||||
$output .= "<div class='column'>" . drupal_render($vars['form']['preview']) . drupal_render($vars['form']['features']) . "</div>";
|
||||
$output .= "</div>";
|
||||
$output .= drupal_render_children($vars['form']);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme a set of features export components.
|
||||
*/
|
||||
function theme_features_form_components(&$vars) {
|
||||
$output = '';
|
||||
foreach (element_children($vars['form']) as $key) {
|
||||
unset($vars['form'][$key]['#title']);
|
||||
$output .= "<div class='features-select features-select-{$key}'>" . drupal_render($vars['form'][$key]) . "</div>";
|
||||
}
|
||||
$output .= drupal_render_children($vars['form']);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme a set of features export components.
|
||||
*/
|
||||
function theme_features_components($vars) {
|
||||
$info = $vars['info'];
|
||||
$sources = $vars['sources'];
|
||||
|
||||
$output = '';
|
||||
$rows = array();
|
||||
$components = features_get_components();
|
||||
if (!empty($info['features']) || !empty($info['dependencies']) || !empty($sources)) {
|
||||
$export = array_unique(array_merge(
|
||||
array_keys($info['features']),
|
||||
array_keys($sources),
|
||||
array('dependencies')
|
||||
));
|
||||
foreach ($export as $component) {
|
||||
if ($component === 'dependencies') {
|
||||
$feature_items = isset($info[$component]) ? $info[$component] : array();
|
||||
}
|
||||
else {
|
||||
$feature_items = isset($info['features'][$component]) ? $info['features'][$component] : array();
|
||||
}
|
||||
$source_items = isset($sources[$component]) ? $sources[$component] : array();
|
||||
if (!empty($feature_items) || !empty($source_items)) {
|
||||
$rows[] = array(array(
|
||||
'data' => isset($components[$component]['name']) ? $components[$component]['name'] : $component,
|
||||
'header' => TRUE
|
||||
));
|
||||
$rows[] = array(array(
|
||||
'data' => theme('features_component_list', array('components' => $feature_items, 'source' => $source_items)),
|
||||
'class' => 'component'
|
||||
));
|
||||
}
|
||||
}
|
||||
$output .= theme('table', array('header' => array(), 'rows' => $rows));
|
||||
$output .= theme('features_component_key', array());
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme individual components in a component list.
|
||||
*/
|
||||
function theme_features_component_list($vars) {
|
||||
$components = $vars['components'];
|
||||
$source = $vars['source'];
|
||||
$conflicts = $vars['conflicts'];
|
||||
|
||||
$list = array();
|
||||
foreach ($components as $component) {
|
||||
// If component is not in source list, it was autodetected
|
||||
if (!in_array($component, $source)) {
|
||||
$list[] = "<span class='features-detected'>". check_plain($component) ."</span>";
|
||||
}
|
||||
elseif (is_array($conflicts) && in_array($component, $conflicts)) {
|
||||
$list[] = "<span class='features-conflict'>". check_plain($component) ."</span>";
|
||||
}
|
||||
else {
|
||||
$list[] = "<span class='features-source'>". check_plain($component) ."</span>";
|
||||
}
|
||||
}
|
||||
foreach ($source as $component) {
|
||||
// If a source component is no longer in the items, it was removed because
|
||||
// it is provided by a dependency.
|
||||
if (!in_array($component, $components)) {
|
||||
$list[] = "<span class='features-dependency'>". check_plain($component) ."</span>";
|
||||
}
|
||||
}
|
||||
return "<span class='features-component-list'>". implode(' ', $list) ."</span>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a themed key for a component list.
|
||||
*/
|
||||
function theme_features_component_key($vars) {
|
||||
$list = array();
|
||||
$list[] = "<span class='features-source'>" . t('Normal') . "</span>";
|
||||
$list[] = "<span class='features-detected'>" . t('Auto-detected') . "</span>";
|
||||
$list[] = "<span class='features-dependency'>" . t('Provided by dependency') . "</span>";
|
||||
return "<span class='features-component-list features-component-key'>" . implode(' ', $list) . "</span>";
|
||||
}
|
Reference in New Issue
Block a user