Parcourir la source

[add] Features contrib module.

opi il y a 8 ans
Parent
commit
08122cfff7
38 fichiers modifiés avec 11587 ajouts et 0 suppressions
  1. 189 0
      sites/all/modules/features/API.txt
  2. 221 0
      sites/all/modules/features/CHANGELOG.txt
  3. 339 0
      sites/all/modules/features/LICENSE.txt
  4. 236 0
      sites/all/modules/features/README.txt
  5. 1656 0
      sites/all/modules/features/features.admin.inc
  6. 553 0
      sites/all/modules/features/features.api.php
  7. 579 0
      sites/all/modules/features/features.css
  8. 1044 0
      sites/all/modules/features/features.drush.inc
  9. 1167 0
      sites/all/modules/features/features.export.inc
  10. 18 0
      sites/all/modules/features/features.info
  11. 151 0
      sites/all/modules/features/features.install
  12. 431 0
      sites/all/modules/features/features.js
  13. 1294 0
      sites/all/modules/features/features.module
  14. 40 0
      sites/all/modules/features/includes/features.block.inc
  15. 115 0
      sites/all/modules/features/includes/features.contact.inc
  16. 54 0
      sites/all/modules/features/includes/features.context.inc
  17. 378 0
      sites/all/modules/features/includes/features.ctools.inc
  18. 73 0
      sites/all/modules/features/includes/features.features.inc
  19. 577 0
      sites/all/modules/features/includes/features.field.inc
  20. 120 0
      sites/all/modules/features/includes/features.filter.inc
  21. 110 0
      sites/all/modules/features/includes/features.image.inc
  22. 163 0
      sites/all/modules/features/includes/features.locale.inc
  23. 427 0
      sites/all/modules/features/includes/features.menu.inc
  24. 174 0
      sites/all/modules/features/includes/features.node.inc
  25. 105 0
      sites/all/modules/features/includes/features.taxonomy.inc
  26. 290 0
      sites/all/modules/features/includes/features.user.inc
  27. 329 0
      sites/all/modules/features/tests/features.test
  28. 43 0
      sites/all/modules/features/tests/features_test/features_test.features.field_base.inc
  29. 76 0
      sites/all/modules/features/tests/features_test/features_test.features.field_instance.inc
  30. 56 0
      sites/all/modules/features/tests/features_test/features_test.features.filter.inc
  31. 64 0
      sites/all/modules/features/tests/features_test/features_test.features.inc
  32. 36 0
      sites/all/modules/features/tests/features_test/features_test.features.taxonomy.inc
  33. 24 0
      sites/all/modules/features/tests/features_test/features_test.features.user_permission.inc
  34. 29 0
      sites/all/modules/features/tests/features_test/features_test.info
  35. 3 0
      sites/all/modules/features/tests/features_test/features_test.module
  36. 38 0
      sites/all/modules/features/tests/features_test/features_test.views_default.inc
  37. 23 0
      sites/all/modules/features/theme/features-admin-components.tpl.php
  38. 362 0
      sites/all/modules/features/theme/theme.inc

+ 189 - 0
sites/all/modules/features/API.txt

@@ -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 - 0
sites/all/modules/features/CHANGELOG.txt

@@ -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 - 0
sites/all/modules/features/LICENSE.txt

@@ -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 - 0
sites/all/modules/features/README.txt

@@ -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)

Fichier diff supprimé car celui-ci est trop grand
+ 1656 - 0
sites/all/modules/features/features.admin.inc


+ 553 - 0
sites/all/modules/features/features.api.php

@@ -0,0 +1,553 @@
+<?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 embedded 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. Alternate it can be an associative array in the
+ *   same style as hook_features_export_files() to add additional files.
+ */
+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);
+    }
+  }
+}
+
+/**
+ * Invoked before a restore operation is run.
+ *
+ * This hook is called before any of the restore operations on the components is
+ * run.
+ *
+ * @param string $op
+ *   The operation that is triggered: revert, rebuild, disable, enable
+ * @param array $items
+ *   The items handled by the operation.
+ */
+function hook_features_pre_restore($op, $items) {
+  if ($op == 'rebuild') {
+    // Use features rebuild to rebuild the features independent exports too.
+    entity_defaults_rebuild();
+  }
+}
+
+/**
+ * Invoked after a restore operation is run.
+ *
+ * This hook is called after any of the restore operations on the components is
+ * run.
+ *
+ * @param string $op
+ *   The operation that is triggered: revert, rebuild, disable, enable
+ * @param array $items
+ *   The items handled by the operation.
+ */
+function hook_features_post_restore($op, $items) {
+  if ($op == 'rebuild') {
+    // Use features rebuild to rebuild the features independent exports too.
+    entity_defaults_rebuild();
+  }
+}
+
+/**
+ * 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('my-node-type', $data)) {
+    $pipe['dependencies'][] = 'mymodule';
+  }
+}
+
+
+/**
+ * Add extra files to the exported file.
+ *
+ * @return array
+ *   An array of files, keyed by file name that will appear in feature and
+ *   with either file_path key to indicate where to copy the file from or
+ *   file_content key to indicate the contents of the file.
+ */
+function hook_features_export_files($module_name, $export) {
+  return array('css/main.css' => array('file_content' => 'body {background-color:blue;}'));
+}
+
+/**
+ * Alter the extra files added to the export.
+ */
+function hook_features_export_files_alter(&$files, $module_name, $export) {
+  $files['css/main.css']['file_content'] = 'body {background-color:black;}';
+}
+
+/**
+ * @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)
+ */
+
+/**
+ * Deprecated as of 7.x-2.0.
+ *
+ * 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 base 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_field_bases_alter(&$fields) {
+}
+
+/**
+ * Alter the field instances 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_field_instances_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) {
+}
+
+/**
+ * @}
+ */

+ 579 - 0
sites/all/modules/features/features.css

@@ -0,0 +1,579 @@
+/**
+ * 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;
+}
+
+.features-lock-icon {
+  display: inline-block;
+}
+
+.features-components h3 {
+  display: inline-block;
+}
+
+.features-lock-empty {
+  display: inline-block;
+  width: 16px;
+}

Fichier diff supprimé car celui-ci est trop grand
+ 1044 - 0
sites/all/modules/features/features.drush.inc


Fichier diff supprimé car celui-ci est trop grand
+ 1167 - 0
sites/all/modules/features/features.export.inc


+ 18 - 0
sites/all/modules/features/features.info

@@ -0,0 +1,18 @@
+name = "Features"
+description = "Provides feature management for Drupal."
+core = 7.x
+package = "Features"
+files[] = tests/features.test
+test_dependencies[] = image
+test_dependencies[] = strongarm
+test_dependencies[] = taxonomy
+test_dependencies[] = views
+
+configure = admin/structure/features/settings
+
+; Information added by Drupal.org packaging script on 2016-04-18
+version = "7.x-2.10"
+core = "7.x"
+project = "features"
+datestamp = "1461011641"
+

+ 151 - 0
sites/all/modules/features/features.install

@@ -0,0 +1,151 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the features module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function features_schema() {
+  $schema['cache_features'] = drupal_get_schema_unprocessed('system', 'cache');
+  $schema['cache_features']['description'] = 'Cache table for features to store module info.';
+  return $schema;
+}
+
+/**
+ * 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');
+  variable_del('features_feature_locked');
+  variable_del('features_lock_mode');
+
+  db_delete('variable')
+    ->condition('name', 'features_admin_show_component_%', 'LIKE')
+    ->execute();
+  db_delete('variable')
+    ->condition('name', 'features_component_locked_%', 'LIKE')
+    ->execute();
+
+  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();
+}
+
+/**
+ * Add {cache_features} table.
+ */
+function features_update_7200() {
+  if (!db_table_exists('cache_features')) {
+    $schema = drupal_get_schema_unprocessed('system', 'cache');
+    db_create_table('cache_features', $schema);
+  }
+}

+ 431 - 0
sites/all/modules/features/features.js

@@ -0,0 +1,431 @@
+/**
+ * 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');
+      });
+
+      //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(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
+              if (matches != null) {
+                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) {
+        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]:not(.processed)', context).addClass('processed').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);
+
+

Fichier diff supprimé car celui-ci est trop grand
+ 1294 - 0
sites/all/modules/features/features.module


+ 40 - 0
sites/all/modules/features/includes/features.block.inc

@@ -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;
+}

+ 115 - 0
sites/all/modules/features/includes/features.contact.inc

@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * @file
+ * Features integration for 'contact' module.
+ */
+
+/**
+ * Implements hook_features_api().
+ */
+function contact_features_api() {
+  return array(
+    'contact_categories' => array(
+      'name' => t('Contact categories'),
+      'feature_source' => TRUE,
+      'default_hook' => 'contact_categories_defaults',
+      'default_file' => FEATURES_DEFAULTS_INCLUDED,
+    ),
+  );
+}
+
+/**
+ * Implements hook_features_export_options().
+ */
+function contact_categories_features_export_options() {
+  $options = array();
+  $categories = db_select('contact', 'c')->fields('c')->execute()->fetchAll();
+  foreach ($categories as $category) {
+    $options["$category->category"] = "$category->category";
+  }
+  return $options;
+}
+
+/**
+ * Implements hook_features_export().
+ */
+function contact_categories_features_export($data, &$export, $module_name = '') {
+  $export['dependencies']['features'] = 'features';
+  $export['dependencies']['contact'] = 'contact';
+
+  foreach ($data as $name) {
+    $export['features']['contact_categories'][$name] = $name;
+  }
+
+  return array();
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function contact_categories_features_export_render($module, $data, $export = NULL) {
+  $render = array();
+  foreach ($data as $name) {
+    $export_category = db_select('contact', 'c')
+      ->fields('c', array('cid', 'category'))
+      ->condition('category', $name, 'LIKE')
+      ->execute()
+      ->fetchAll();
+    if (isset($export_category[0]->cid) && ($category = contact_load($export_category[0]->cid))) {
+      unset($category['cid']);
+      $render[$name] = $category;
+    }
+  }
+  return array('contact_categories_defaults' => '  return ' . features_var_export($render, '  ') . ';');
+}
+
+/**
+ * Implements hook_features_revert().
+ */
+function contact_categories_features_revert($module) {
+  return contact_categories_features_rebuild($module);
+}
+
+/**
+ * Implements hook_features_rebuild().
+ */
+function contact_categories_features_rebuild($module) {
+  if ($defaults = features_get_default('contact_categories', $module)) {
+    foreach ($defaults as $default_category) {
+      $existing_categories = db_select('contact', 'c')
+        ->fields('c', array('cid', 'category'))
+        ->execute()
+        ->fetchAll();
+      if ($existing_categories) {
+        foreach ($existing_categories as $existing_category) {
+          if ($default_category['category'] == $existing_category->category) {
+            db_update('contact')
+              ->fields(
+                array(
+                  'recipients' => $default_category['recipients'],
+                  'reply' => $default_category['reply'],
+                  'weight' => $default_category['weight'],
+                  'selected' => $default_category['selected'],
+                )
+              )
+              ->condition('category', $existing_category->category, '=')
+              ->execute();
+          }
+          else {
+            db_merge('contact')
+              ->key(array('category' => $default_category['category']))
+              ->fields($default_category)
+              ->execute();
+          }
+        }
+      }
+      else {
+        db_merge('contact')
+          ->key(array('category' => $default_category['category']))
+          ->fields($default_category)
+          ->execute();
+      }
+    }
+  }
+}

+ 54 - 0
sites/all/modules/features/includes/features.context.inc

@@ -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;
+}

+ 378 - 0
sites/all/modules/features/includes/features.ctools.inc

@@ -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'];
+      }
+    }
+  }
+}

+ 73 - 0
sites/all/modules/features/includes/features.features.inc

@@ -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);
+    }
+  }
+}

+ 577 - 0
sites/all/modules/features/includes/features.field.inc

@@ -0,0 +1,577 @@
+<?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['foreign keys']);
+      // 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 no one 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_prefix = '  // Exported field_base: ';
+      $field_identifier = features_var_export($identifier);
+      if (features_field_export_needs_wrap($field_prefix, $field_identifier)) {
+        $code[] = rtrim($field_prefix);
+        $code[] = "  // {$field_identifier}.";
+      }
+      else {
+        $code[] = $field_prefix . $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_prefix = '  // Exported field_instance: ';
+      $instance_identifier = features_var_export($identifier);
+      if (features_field_export_needs_wrap($instance_prefix, $instance_identifier)) {
+        $code[] = rtrim($instance_prefix);
+        $code[] = "  // {$instance_identifier}.";
+      }
+      else {
+        $code[] = $instance_prefix . $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']];
+        $array_diff_result = drupal_array_diff_assoc_recursive($field + $existing_field, $existing_field);
+        if (!empty($array_diff_result)) {
+          try {
+            field_update_field($field);
+          }
+          catch (FieldException $e) {
+            watchdog('features', 'Attempt to update field %label failed: %message', array('%label' => $field['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
+          }
+        }
+      }
+      else {
+        try {
+          field_create_field($field);
+        }
+        catch (FieldException $e) {
+          watchdog('features', 'Attempt to create field %label failed: %message', array('%label' => $field['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
+        }
+        $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 no one 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']];
+        $array_diff_result = drupal_array_diff_assoc_recursive($field_config + $existing_field, $existing_field);
+        if (!empty($array_diff_result)) {
+          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;
+}
+
+/**
+ * Determine if a field export line needs to be wrapped.
+ *
+ * Drupal code standards specify that comments should wrap at 80 characters or
+ * less.
+ *
+ * @param string $prefix
+ *   The prefix to be exported before the field identifier.
+ * @param string $identifier
+ *   The field identifier.
+ *
+ * @return BOOL
+ *   TRUE if the line should be wrapped after the prefix, else FALSE.
+ *
+ * @see https://www.drupal.org/node/1354
+ */
+function features_field_export_needs_wrap($prefix, $identifier) {
+  // Check for 79 characters, since the comment ends with a full stop.
+  return (strlen($prefix) + strlen($identifier) > 79);
+}

+ 120 - 0
sites/all/modules/features/includes/features.filter.inc

@@ -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;
+}

+ 110 - 0
sites/all/modules/features/includes/features.image.inc

@@ -0,0 +1,110 @@
+<?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(array &$style) {
+  // Sanitize style: Don't export numeric IDs and things which get overwritten
+  // in image_styles() or are code/storage specific. The name property will be
+  // the key of the exported $style array.
+  $style = array_diff_key($style, array_flip(array(
+    'isid',
+    'name',
+    'module',
+    'storage',
+  )));
+
+  // Sanitize effects: all that needs to be kept is name, weight and data,
+  // which holds all the style-specific configuration. Other keys are assumed
+  // to belong to the definition of the effect itself, so not configuration.
+  foreach ($style['effects'] as $id => $effect) {
+    $style['effects'][$id] = array_intersect_key($effect, array_flip(array(
+      'name',
+      'data',
+      'weight',
+    )));
+  }
+}

+ 163 - 0
sites/all/modules/features/includes/features.locale.inc

@@ -0,0 +1,163 @@
+<?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,
+        'weight' => empty($language->weight) ? 0 : $language->weight,
+      ))
+      ->condition('language', $language->language)
+      ->execute();
+  }
+  // Update Existing language.
+  else {
+    // Get field list from table schema.
+    $properties = drupal_schema_fields_sql('languages');
+    // 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);
+    }
+  }
+}

+ 427 - 0
sites/all/modules/features/includes/features.menu.inc

@@ -0,0 +1,427 @@
+<?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[] = '  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
+        $temp = $link;
+        menu_link_save($temp);
+      }
+
+       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'];
+    }
+  }
+  $code[] = '';
+  if (!empty($translatables)) {
+    $code[] = features_translatables_export($translatables, '  ');
+  }
+
+  $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'] : '';
+          $weight = 0;
+          // Parent has been seen, so weigh this above parent.
+          if (isset($ordered[$parent])) {
+            $weight = $ordered[$parent] + 1;
+          }
+          // Next loop will try to find parent weight instead.
+          elseif ($parent) {
+            continue;
+          }
+          $ordered[$identifier] = $weight;
+          $all_links[$identifier] = $link;
+          unset($unordered[$key]);
+        }
+      // Exit out when the above does no changes this loop.
+      } while (count($unordered) < $current);
+    }
+    // Add all remaining unordered items to the ordered list.
+    foreach ($unordered as $link) {
+      $identifier = menu_links_features_identifier($link);
+      $ordered[$identifier] = 0;
+      $all_links[$identifier] = $link;
+    }
+    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)
+    ->addTag('features_menu_link')
+    ->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);
+      // Links with a stored identifier must only be matched on that identifier,
+      // to prevent cross over assumptions.
+      if (isset($link->options['identifier'])) {
+        if (strcmp($link->options['identifier'], $identifier) == 0) {
+          return (array)$link;
+        }
+      }
+      elseif ((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));
+}

+ 174 - 0
sites/all/modules/features/includes/features.node.inc

@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * Implements hook_features_api().
+ */
+function node_features_api() {
+  return array(
+    'node' => array(
+      'name' => t('Content types'),
+      'feature_source' => TRUE,
+      'default_hook' => 'node_info',
+      'alter_type' => FEATURES_ALTER_TYPE_INLINE,
+    ),
+  );
+}
+
+/**
+ * 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[] = '  drupal_alter(\'node_info\', $items);';
+  $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_feature().
+ *
+ * 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_feature($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;
+      $type_info->disabled = 0;
+      node_type_save($type_info);
+    }
+  }
+}
+
+/**
+ * Implements hook_features_enable_feature().
+ *
+ * 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.
+ *
+ * Update the database cache of node types if needed.
+ *
+ * @param $module
+ *   Name of module that has been enabled.
+ */
+function node_features_enable_feature($module) {
+  if ($default_types = features_get_default('node', $module)) {
+    $rebuild = FALSE;
+    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;
+        $type_info->disabled = 0;
+        node_type_save($type_info);
+      }
+      else {
+        $rebuild = TRUE;
+      }
+    }
+    if ($rebuild) {
+      node_types_rebuild();
+    }
+  }
+}

+ 105 - 0
sites/all/modules/features/includes/features.taxonomy.inc

@@ -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);
+    }
+  }
+}

+ 290 - 0
sites/all/modules/features/includes/features.user.inc

@@ -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] = $module_info[$module]['name'];
+  }
+  ksort($modules);
+
+  $options = array();
+  foreach ($modules as $module => $display_name) {
+    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;
+}

+ 329 - 0
sites/all/modules/features/tests/features.test

@@ -0,0 +1,329 @@
+<?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 integration 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.');
+      }
+    }
+  }
+}
+
+
+/**
+ * Test detecting modules as features. 
+ */
+class FeaturesDetectionTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  /**
+   * Test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Feature Detection tests'),
+      'description' => t('Run tests for detecting items as features.') ,
+      'group' => t('Features'),
+    );
+  }
+
+  /**
+   * Set up test.
+   */
+  public function setUp() {
+    parent::setUp(array(
+      'features',
+    ));
+  }
+
+  /**
+   * Run test.
+   */
+  public function test() {
+    module_load_include('inc', 'features', 'features.export');
+    // First test that features_populate inserts the features api key.
+    $export = features_populate(array(), array(), 'features_test_empty_fake');
+    $this->assertTrue(!empty($export['features']['features_api']) && key($export['features']['features_api']) == 'api:' . FEATURES_API, 'Features API key added to new export.');
+    $this->assertTrue((bool)features_get_features('features_test'), 'Features test recognized as a feature.');
+    $this->assertFalse((bool)features_get_features('features'), 'Features module not recognized as a feature.');
+  }
+}

+ 43 - 0
sites/all/modules/features/tests/features_test/features_test.features.field_base.inc

@@ -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;
+}

+ 76 - 0
sites/all/modules/features/tests/features_test/features_test.features.field_instance.inc

@@ -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;
+}

+ 56 - 0
sites/all/modules/features/tests/features_test/features_test.features.filter.inc

@@ -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;
+}

+ 64 - 0
sites/all/modules/features/tests/features_test/features_test.features.inc

@@ -0,0 +1,64 @@
+<?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(
+    'effects' => array(
+      2 => array(
+        '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;
+}

+ 36 - 0
sites/all/modules/features/tests/features_test/features_test.features.taxonomy.inc

@@ -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',
+          ),
+        ),
+      ),
+    ),
+  );
+}

+ 24 - 0
sites/all/modules/features/tests/features_test/features_test.features.user_permission.inc

@@ -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;
+}

+ 29 - 0
sites/all/modules/features/tests/features_test/features_test.info

@@ -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 2016-04-18
+version = "7.x-2.10"
+core = "7.x"
+project = "features"
+datestamp = "1461011641"
+

+ 3 - 0
sites/all/modules/features/tests/features_test/features_test.module

@@ -0,0 +1,3 @@
+<?php
+
+include_once('features_test.features.inc');

+ 38 - 0
sites/all/modules/features/tests/features_test/features_test.views_default.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;
+}

+ 23 - 0
sites/all/modules/features/theme/features-admin-components.tpl.php

@@ -0,0 +1,23 @@
+<?php
+?>
+<div class='clearfix features-components'>
+  <div class='column'>
+    <div class='info'>
+      <?php print $lock_feature ?><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>

+ 362 - 0
sites/all/modules/features/theme/theme.inc

@@ -0,0 +1,362 @@
+<?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;
+  $vars['lock_feature'] = theme('features_lock_link', array('feature' => $form['#feature']->name));
+}
+
+/**
+ * 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 lock link
+ */
+function theme_features_lock_link($vars) {
+  drupal_add_library('system', 'ui');
+  drupal_add_library('system', 'drupal.ajax');
+  $component = $vars['component'] ? $vars['component'] : '';
+  if ($component && features_component_is_locked($component)) {
+    return l(t('Component locked'), 'admin/structure/features/settings', array(
+      'attributes' => array(
+        'class' => 'features-lock-icon ui-icon ui-icon-locked',
+        'title' => t('This component is locked on a global level.'),
+      ),
+      'fragment' => 'edit-lock-components',
+    ));
+  }
+  $feature = $vars['feature'];
+  $is_locked = features_feature_is_locked($feature, $component);
+  $options = array(
+    'attributes' => array(
+      'class' => array('use-ajax features-lock-icon ui-icon ' . ($is_locked ? ' ui-icon-locked' : ' ui-icon-unlocked')),
+      'id' => 'features-lock-link-' . $feature . ($component ? '-' . $component : ''),
+      'title' => $is_locked ? t('This item is locked and features will not be rebuilt or reverted.') : t('This item is unlocked and will be rebuilt/reverted as normal.'),
+    ),
+    'query' => array('token' => drupal_get_token('features/' . $feature . '/' . $component)),
+  );
+  $path =  "admin/structure/features/" . $feature . "/lock/nojs" . ($component ? '/' . $component: '');
+  return l($is_locked ? t('UnLock') : t('Lock'), $path, $options);
+}
+
+/**
+ * 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>";
+}