Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Using the Features API


Published on

A presentation on the Features API which shows how to use this to make your application Features friendly.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Using the Features API

  1. 1. The Features APIMaking your modules Feature friendly.
  2. 2. What is the API thing?Features is designed to capture module“state” information and allows it to bereproduced on any server.The features API allows module coders toadd functions that allow this “State” to betransferred.Reference: API.txt file in Features module
  3. 3. Some TerminologyFeature module – A special module that captures the “state” ofmodule components needed for the function it defines. E.g.profilesComponent Type – A grouping of configuration objectsComponent – A configuration object that can be exported, e.g. aview, content type, or CCK field. Components are listed in the“edit component” field of the create feature page.Machine Name – An identifier that is unique in and across sites(as opposed to an DB id field which unique only on a site).Exportable component – An exportable setting that can existonly in code. E.g. a view.Faux-Exportable component – a exportable setting that needsto be in the db. Settings of this type are used to synchronizeentries in the DB using machine names
  4. 4. Feature Component HashesThe Feature module code maintains various md5 hashesthat are use to compare states. Basically these hashesare for: The state defined by the site settings The state defined by the current feature module code The last known state set by feature module codeThese are used to generate the component status shownin the Features management page.
  5. 5. Feature StatesThe status of the Feature component as displayed on theManage Features pages.Default – All three component hashes matchOverridden – Site settings do not matchNeeds Review – All three hashes are different. E.g., local overrides with new feature code.Rebuildable – Site settings match previous settings but the feature code is new.Rebuilding – A rarely “seen” transient state that is set when a component is being synced with DB.
  6. 6. The API Hooks-`hook_features_api()` defines the component types- `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 (as defined in code) state.-`hook_features_rebuild()` updates faux-exportable components back to their default state.Reference: features.api.php in Features module code
  7. 7. A Case StudyNeed:The ldapauth module (part of LDAP Integration suite) hassome complex settings that often need to be duplicateacross a lot of server. Features support is a great way to dothis.Requirements:Transfer the module settings that fall into two types:- Global settings stored as persistent variables- LDAP server definitions (can be more than one) are stored in the ldapauth table.
  8. 8. A Case StudyStrategy: Create two Features component types to support the two kinds of settings. Utilize Strongarm and Ctools API/Code to simplify codingIssues: The LDAP Server settings use DB generated id fields that do not map across server.
  9. 9. Adding Machine Name SupportIn order to make the LDAP server configuration entriesexportable, they needed to support a cross site machinename. This was done by:• Adding a Machine name field to the ldapauth DB• Adding code to retrieve/update ldapauth table entries by machine name ( ldapauth_server_load() )• Modifying the admin screen to allow for autogeneration / editing of machine names. ( ldapauth_admin_form() )• Modifying the initial install process to include this new field. ( ldapauth_schema() )• Modifying the update process to add field and populate it. ( ldapauth_update_6005() )
  10. 10. Define the Component TypesThe part of the Features API to implement is hook_features_api(). Here’sthe one for ldapauth:function ldapauth_features_api() { $info = array( ldap_servers => array( name => LDAP Integration, default_hook => default_ldap_servers, default_file => FEATURES_DEFAULTS_INCLUDED_COMMON, feature_source => TRUE, file => drupal_get_path(module‘,ldapauth‘)./, ) ); if ( module_exists(strongarm) ) { $info[ldap_settings] = array( name => LDAP Integration, default_hook => default_ldap_settings, default_file => FEATURES_DEFAULTS_INCLUDED_COMMON, feature_source => TRUE, file => drupal_get_path(module‘,ldapauth‘)./, ); } return $info;}
  11. 11. Component Type OptionsFor each component type, the <component>_features_export_options()hook needs to be implements. This tells the Features management GUIwhat components to display under each type.function ldap_servers_features_export_options() { module_load_include(inc, ldapauth, includes/ldap.core); $options = array(); $servers = ldapauth_server_load_all(); foreach ( $servers as $server ) { $options[$server->machine_name] = $server->name; } return $options;}function ldap_settings_features_export_options() { $info = array( ldapauth_login_process’=>t(Authentication‘).: ‘.t(Authentication mode), ldapauth_login_conflict’=>t(Authentication‘).: ‘.t(User conflict resolve pr ldapauth_forget_passwords’=>t(Authentication‘).: ‘.t(Store user passwords) ldapauth_sync_passwords’=>t(Authentication‘).: ‘.t(Sync passwords), ldapauth_create_users’=>t(Authentication‘).: ‘.t(Create new users), ldapauth_alter_username_field’ =>t(Authentication‘).: ‘.t(Alter user name f ldapauth_disable_pass_change’=>t(Authentication‘).: ‘.t(Remove password fie ldapauth_alter_email_field’=>t(Authentication‘).: ‘.t(Alter email field), ); ldapauth_submodules("ldap_settings", $info); return $info;}
  12. 12. Features Export ListThe features_export hook generates the information that goes into thefeatures module .info file at build time. Each component type needs one.For the LDAP Global settings, we just define the module dependenciesand them make use of Strongarm’s variable_features_export() function.function ldap_settings_features_export($data, &$export, $module_name = "") { $export[dependencies][ldapauth] = ldapauth; $submodules = ldapauth_submodules(ldap_settings); foreach ( $submodules as $submodule ) { if ( module_exists($submodule ) ) { $export[dependencies][$submodule] = $submodule; } } return variable_features_export($data, $export, $module_name);}
  13. 13. Features Export ListFor the LDAP Server setting, we define the module dependenciesand return a list of server definition machine names that the user hasselected in the GUI.function ldap_servers_features_export($data, &$export, $module_name = "") { module_load_include(inc, ldapauth, includes/ldap.core); $export[dependencies][ldapauth] = ldapauth; $submodules = ldapauth_submodules(ldap_servers); foreach ( $submodules as $submodule ) { if ( module_exists($submodule ) ) { $export[dependencies][$submodule] = $submodule; } } foreach ( $data as $component ) { $export[features][ldap_servers][$component] = $component; } return array();}
  14. 14. Features Export RenderThe features_export_render hook generates the information that goesinto the features module code file(s) at build time. Each component typeneeds one.This feature is also used to generate the md5 hash tag to check foroverride and other status.For the LDAP Global settings, we just make use of Strongarm’svariable_features_export_render() function.function ldap_settings_features_export_render($module, $data) { return variable_features_export_render($module, $data);}
  15. 15. Features Export RenderFor the LDAP Server settings, we need to generate the code to create aservers array with the machine name as a key and it’s settings as a value.function ldap_servers_features_export_render($module, $data, $export) { module_load_include(inc, ldapauth, includes/ldap.core); $code = array(); $code[] = $servers = array();; if ( is_null($export) ) {// If this is an override check, export all current servers $servers = ldapauth_server_load_all(TRUE); foreach ($servers as $server) { unset($server->sid); $server = (array) $server; ksort($server); // Sort because updated tables dont match, new tables $code[] =" $servers[{$server[machine_name]}] = “.features_var_export($server, ‘ ‘)." } } else { foreach ($data as $name) { $server = ldapauth_server_load($name); unset($server->sid); $server = (array) $server; ksort($server); $code[] = " $servers[{$name}] = " . features_var_export($server, ) . ";"; } } $code[] = return $servers;; $code = implode("n", $code); return array(default_ldap_servers => $code );}The check for import or override check is because if there is a new server entry,it should be flagged as an override condition.
  16. 16. Features RebuildThe features_rebuild hook is called when a feature is first installed.Each component type needs one.For the LDAP Global settings, we just make use of Strongarm’svariable_features_rebuild () function.function ldap_settings_features_rebuild($module) { variable_features_rebuild($module);}For the LDAP server settings, we need to get the settings in code and add anyNON-existent server.function ldap_servers_features_rebuild($module) { module_load_include(inc, ldapauth, includes/ldap.core); $servers = module_invoke($module, default_ldap_servers); foreach ($servers as $server) { $existing_server = ldapauth_server_load($server[machine_name]); if ( empty($existing_server) ) { ldapauth_server_save($server); } }}
  17. 17. Features RevertThe features_revert hook is called when a feature is“reverted” to the settings defined in the feature codevia the admin GUI.For the LDAP Global settings, we just make use ofStrongarm’s function.function ldap_settings_features_rebuild($module) { variable_features_revert($module);}
  18. 18. Features Revert For the LDAP server settings, any server entry not defined in the code needs to be deleted. Any existing server entry that is defined in code definition needs to have it settings update to match the code. Finally, any server entry defined in the code but not in the DB need to be created.function ldap_servers_features_revert($module) { module_load_include(inc, ldapauth, includes/ldap.core); $default_servers = module_invoke($module, default_ldap_servers); $servers = ldapauth_server_load_all(TRUE); $existing_servers = array(); // Update any existing servers in feature and delete those not in the feature. foreach ( $servers as $server ) { $machine_name = $server->machine_name; $existing_servers[$machine_name] = $machine_name; if ( isset($default_servers[$machine_name]) ) { // Update servers $default_servers[$machine_name][sid] = $server->sid; ldapauth_server_save($default_servers[$machine_name], TRUE); } else { // Delete server ldapauth_server_delete($server, TRUE); } } // Add any servers in the feature that dont exist. foreach ( $default_servers as $server ) { if ( ! isset($existing_servers[$server[machine_name]]) ) { // Add in default server not in unset($server[sid]); ldapauth_server_save($server, TRUE); } }}
  19. 19. The End Product
  20. 20. Questions?