Example #1
0
    /**
     * Return the main body of the file code.
     */
    function code_body()
    {
        $component_data = $this->getRootComponentData();
        $mb_factory = module_builder_get_factory();
        // Sanity checks already done at this point; no need to catch exception.
        $mb_task_handler_analyze = $mb_factory->getTask('AnalyzeModule');
        $hooks = $mb_task_handler_analyze->getInventedHooks($component_data['module_root_name']);
        $module_root_name = $component_data['module_root_name'];
        $module_root_name_title_case = ucfirst($component_data['module_root_name']);
        $module_readable_name = $component_data['module_readable_name'];
        // Build an array of code pieces.
        $code_pieces = array();
        // The docblock grouping.
        $code_pieces['group'] = <<<EOT
/**
 * @addtogroup hooks
 * @{
 */

EOT;
        foreach ($hooks as $hook_short_name => $parameters) {
            $code_pieces[$hook_short_name] = $this->hook_code($hook_short_name, $parameters);
        }
        return $code_pieces;
    }
Example #2
0
 /**
  * Gather hook documentation files.
  *
  * This retrieves a list of api hook documentation files from the current
  * Drupal install. On D7 these are files of the form MODULE.api.php and are
  * present in the codebase (rather than needing to be downloaded from an
  * online code repository viewer as is the case in previous versions of
  * Drupal).
  */
 protected function gatherHookDocumentationFiles()
 {
     // Get the hooks directory.
     $mb_factory = module_builder_get_factory();
     $directory = $mb_factory->environment->hooks_directory;
     // Get Drupal root folder as a file path.
     // DRUPAL_ROOT is defined both by Drupal and Drush.
     // @see _drush_bootstrap_drupal_root(), index.php.
     $drupal_root = DRUPAL_ROOT;
     $system_listing = $mb_factory->environment->systemListing('/\\.api\\.php$/', 'modules', 'filename');
     // returns an array of objects, properties: uri, filename, name,
     // keyed by filename, eg 'comment.api.php'
     // What this does not give us is the originating module!
     //print_r($system_listing);
     foreach ($system_listing as $filename => $file) {
         // Extract the module name from the path.
         // WARNING: this is not always going to be correct: will fail in the
         // case of submodules. So Commerce is a big problem here.
         // We could instead assume we have MODULE.api.php, but some modules
         // have multiple API files with suffixed names, eg Services.
         // @todo: make this more robust, somehow!
         $matches = array();
         preg_match('@modules/(?:contrib/)?(\\w+)@', $file->uri, $matches);
         //print_r($matches);
         $module = $matches[1];
         //dsm($matches, $module);
         // Copy the file to the hooks directory.
         copy($drupal_root . '/' . $file->uri, $directory . '/' . $file->filename);
         $hook_files[$filename] = array('original' => $drupal_root . '/' . $file->uri, 'path' => $directory . '/' . $file->filename, 'destination' => '%module.module', 'group' => $module, 'module' => $module);
     }
     // We now have the basics.
     // We should now see if some modules have extra information for us.
     $this->getHookDestinations($hook_files);
     return $hook_files;
 }
Example #3
0
 /**
 * Get list of hook file URLS from any modules that declare them.
 *
 * @param $directory
 *  The path to the module builder hooks directory.
 *
 * @return
 *   An array of data about the files to download, keyed by (safe) filename:
    [system.core.php] => Array
      [path] => the full path this file should be saved to
      [url] => URL
      [destination] => %module.module
      [group] => core
 */
 function getHookFileUrls($directory)
 {
     // Get data by invoking our hook.
     $mb_factory = module_builder_get_factory();
     $data = $mb_factory->environment->invokeInfoHook();
     foreach ($data as $module => $module_data) {
         $branch = $module_data['branch'];
         foreach ($module_data['hook_files'] as $hook_file => $destination) {
             $url = str_replace(array('%file', '%branch'), array($hook_file, $branch), $module_data['url']);
             // Create our own safe filename with module prefix.
             $hook_file_safe_name = "{$module}.{$hook_file}";
             $urls[$hook_file_safe_name]['path'] = $directory . '/' . $hook_file_safe_name;
             $urls[$hook_file_safe_name]['url'] = $url;
             $urls[$hook_file_safe_name]['destination'] = $destination;
             if (isset($module_data['hook_destinations'])) {
                 $urls[$hook_file_safe_name]['hook_destinations'] = array();
                 foreach ($module_data['hook_destinations'] as $destination => $hooks) {
                     $urls[$hook_file_safe_name]['hook_destinations'] += array_fill_keys($hooks, $destination);
                 }
             }
             if ($module_data['group'] == '#filenames') {
                 $urls[$hook_file_safe_name]['group'] = str_replace('.php', '', $hook_file);
             } else {
                 $urls[$hook_file_safe_name]['group'] = $module_data['group'];
             }
         }
     }
     //print_r($urls);
     return $urls;
 }
Example #4
0
 /**
  * Constructor method; sets the component data.
  *
  * @param $component_name
  *   The identifier for the component.
  * @param $component_data
  *   (optional) An array of data for the component. Any missing properties
  *   (or all if this is entirely omitted) are given default values. Valid
  *   properties are:
  *    - 'class': The name of the annotation class that defines the plugin
  *      type, e.g. 'Drupal\Core\Entity\Annotation\EntityType'.
  *      TODO: since the classnames are unique regardless of namespace, figure
  *      out if there is a way of just specifying the classname.
  */
 function __construct($component_name, $component_data = array())
 {
     // Set some default properties.
     $component_data += array();
     $mb_factory = module_builder_get_factory('ModuleBuilderEnvironmentDrush');
     $mb_task_handler_report_plugins = $mb_factory->getTask('ReportPluginData');
     $plugin_data = $mb_task_handler_report_plugins->listPluginData();
     $plugin_data = $plugin_data[$component_name];
     $component_data['plugin_type_data'] = $plugin_data;
     parent::__construct($component_name, $component_data);
 }
 /**
  * Declares the subcomponents for this component.
  *
  * These are not necessarily child classes, just components this needs.
  *
  * A hook implementation adds the module code file that it should go in. It's
  * safe for the same code file to be requested multiple times by different
  * hook implementation components.
  *
  * @return
  *  An array of subcomponent names and types.
  */
 protected function requiredComponents()
 {
     // TODO! Caching!
     $mb_factory = module_builder_get_factory();
     // Sanity checks already done at this point; no need to catch exception.
     $mb_task_handler_report = $mb_factory->getTask('ReportHookData');
     $hook_function_declarations = $mb_task_handler_report->getHookDeclarations();
     //drush_print_r($hook_function_declarations[$this->name]);
     $this->hook_info = $hook_function_declarations[$this->name];
     $filename = $hook_function_declarations[$this->name]['destination'];
     $this->code_file = $filename;
     return array($filename => 'ModuleCodeFile');
 }
 /**
  * Helper function for our requiredComponents().
  *
  * (Move this back out if it needs to be used by other components in future?)
  *
  * Returns an array of hook data and templates for the requested hooks.
  * This is handled live rather than in process.inc to allow the user to alter
  * their custom hook templates.
  *
  * @return
  *   An array whose keys are destination filenames with the token '%module',
  *   and whose values are arrays of hook data. The hook data keys are:
  *    - declaration: The function declaration, with the 'hook' part not
  *        yet replaced.
  *    - destination: The destination, with tokens still in place.
  *    - template_files: A list of template file types, in order of preference,
  *        keyed by filename and with the value TRUE if the hook code exists
  *        in that template file.
  *    - template (optional): The template code, if any was found.
  *   Example:
  *  'destination file' => array(
  *    'hook_foo' => array('declaration' => DATA, 'template' => DATA)
  */
 function getTemplates($module_data)
 {
     $mb_factory = module_builder_get_factory();
     // Sanity checks already done at this point; no need to catch exception.
     $mb_task_handler_report = $mb_factory->getTask('ReportHookData');
     // Build a clean list of the requested hooks, by filtering out the keys
     // with 0 values that come from UI form.
     $requested_hooks = array_filter($module_data['hooks']);
     //print_r($requested_hooks);
     // TODO: might not need this; easier to test truth than isset.
     // Get array of the hook function declarations from the downloaded hook data.
     // This is a complete list of all hooks that exist.
     // In the form: 'hook_foo' => array('declaration', 'destination')
     // This array is the order they are in the files from d.org: alphabetical apparently.
     // We don't care for this order!
     $hook_function_declarations = $mb_task_handler_report->getHookDeclarations();
     // If we get NULL then no hook data exists: return NULL again.
     // TODO: check this sort of error at an earlier stage!
     if (is_null($hook_function_declarations)) {
         return NULL;
     }
     //drush_print_r($hook_function_declarations);
     // TODO: this should contain the name of the api.php file that provided it!
     // Add hook dependencies.
     foreach (array_keys($requested_hooks) as $hook_name) {
         if (!empty($hook_function_declarations[$hook_name]['dependencies'])) {
             //drush_print_r($hook_function_declarations[$hook_name]['dependencies']);
             foreach ($hook_function_declarations[$hook_name]['dependencies'] as $hook_dependency) {
                 $requested_hooks[$hook_dependency] = TRUE;
             }
         }
     }
     // Trim this down to just the ones we care about.
     // By this point, both sets of array keys are standardized to lower case.
     $hook_function_declarations = array_intersect_key($hook_function_declarations, $requested_hooks);
     //print_r("hook_function_declarations: \n");
     //drush_print_r($hook_function_declarations);
     // Filter out the requested hooks that don't have definitions.
     // We do this now as it's possible for a hook to have no definition because
     // the user doesn't have it, but have a template because we provide it,
     // eg views_api.
     // We do this by hand this time rather than array_intersect_key() so we can
     // make a list of hooks we're rejecting for (TODO!) eventual warning output.
     $rejected_hooks = array();
     foreach (array_keys($requested_hooks) as $hook_name) {
         if (!isset($hook_function_declarations[$hook_name])) {
             unset($requested_hooks[$hook_name]);
             $rejected_hooks[] = $hook_name;
         }
     }
     // TODO: at this point we should check if we have any actual hooks to
     // process, and warn if not.
     // We should probably also do something with rejected hooks list.
     // Include generating component file, for parsing templates.
     $mb_factory->environment->loadInclude('process');
     // Step 1:
     // Build up a list of the basic template files we want to parse.
     //  - in each $hook_function_declarations item, place an ordered list of
     //    all potential template files. We will set these to TRUE in step 2
     //    if they hold a template for the hook.
     //  - meanwhile, build up list of template files we will want to check for
     //    existence and parse.
     // Template filenames are of the following form, in the order they should be
     // checked, ie from most specific to most general:
     //  - GROUP.hooks.template, eg node.hooks.template
     //    (Though groups are still TODO: this is scaffold only for now!)
     //  - FILENAME.template, where the modulename is replaced with 'hooks', hence
     //    hooks.module.template, hooks.install.template, hooks.views.inc.template.
     //  - hooks.template - the base template, final fallback
     // These are found in module_builder/templates/VERSION, and
     // in addition, a file may be overridden by being present in the user's
     // data directory. Though just what the 'data directory' means exactly is
     // not yet properly defined...
     $template_file_names = array();
     foreach ($hook_function_declarations as $hook_name => $hook) {
         // TODO: $groupname_template = 'GROUP.hooks.template';
         $filename_template = str_replace('%module', 'hooks', $hook['destination']) . '.template';
         // Place in each $hook_function_declarations item an ordered list of
         // potential files from best fit to fallback.
         // These are keyed by filename and all with value FALSE initially.
         $hook_function_declarations[$hook_name]['template_files'] = array_fill_keys(array($filename_template, 'hooks.template'), FALSE);
         // Meanwhile, build up list of files we will want to check for existence and parse.
         // TODO: $template_file_names[$groupname_template] = TRUE;
         $template_file_names[$filename_template] = TRUE;
         $template_file_names['hooks.template'] = TRUE;
     }
     // print_r("template file names: \n");
     // print_r($template_file_names);
     // print_r("hook_function_declarations are now:: \n");
     // print_r($hook_function_declarations);
     // Step 2:
     // Now we parse the templates we need.
     // We look in two places: module_builder's own '/templates' folder, and the optional
     // location given for user data (the latter is in fact TODO...)
     // User templates override on a per-file basis, so a custom
     // node.hooks.template will only override that same file in the module data;
     // if the hook is not requested as part of a group then that file will not be considered.
     // (Though groups are broken for now...)
     $version = $mb_factory->environment->major_version;
     $template_base_path_module = $mb_factory->environment->getPath('templates') . '/' . $version;
     //print "base path: $template_base_path_module";
     // $template_base_paths['module']
     // $template_base_paths['user']
     $template_data = array();
     foreach (array_keys($template_file_names) as $filename) {
         $filepath = "{$template_base_path_module}/{$filename}";
         if (file_exists($filepath)) {
             $template_file = file_get_contents($filepath);
             $template_data = module_builder_parse_template($template_file);
             // Trim the template data to the hooks we care about.
             $template_data = array_intersect_key($template_data, $requested_hooks);
             // Flag the template file in the hook list; ie, set to TRUE the template
             // file in the list which we first created as entirely FALSE.
             foreach (array_keys($template_data) as $hook_name) {
                 $hook_function_declarations[$hook_name]['template_files'][$filename] = TRUE;
             }
         }
     }
     //print_r("hook_function_declarations now have template files \n");
     //print_r($hook_function_declarations);
     // $template_data is now an array of the form:
     //  [hook name] => array('template' => DATA)
     // in a pretty order which we want to hold on to.
     //print_r('template data is:');
     //print_r($template_data);
     // Step 3a:
     // Build a new array of hook data, so that we take the order from the
     // template data, but using the same data structure as the
     // $hook_function_declarations array.
     // The big question here: once we have other template files, such as those
     // for groups, or user ones, how do we combine the order from all of them?
     // Or do we just have an overall order from the template files' order, and
     // then within that respect each of theirs, so in effect it's like
     // concatenating all the template files we use?
     $hook_data_return = array();
     foreach (array_keys($template_data) as $hook_name) {
         $destination = $hook_function_declarations[$hook_name]['destination'];
         // Copy over the data we already had.
         $hook_data_return[$destination][$hook_name] = $hook_function_declarations[$hook_name];
         // Copy over the template.
         // TODO: more here.
         $hook_data_return[$destination][$hook_name]['template'] = $template_data[$hook_name]['template'];
     }
     // Step 3b:
     // Not all hooks have template data, so fill these in too.
     foreach ($hook_function_declarations as $hook_name => $hook) {
         $destination = $hook_function_declarations[$hook_name]['destination'];
         if (!isset($hook_data_return[$destination][$hook_name])) {
             $hook_data_return[$destination][$hook_name] = $hook_function_declarations[$hook_name];
         }
         // We have no template data, so fill in the sample from the api.php file,
         // as this is often informative.
         if (empty($hook_data_return[$destination][$hook_name]['template'])) {
             $hook_data_return[$destination][$hook_name]['template'] = $hook_function_declarations[$hook_name]['body'];
         }
     }
     //print_r('step 3:');
     //print_r($hook_data_return);
     return $hook_data_return;
 }
Example #7
0
 /**
  * Define the component data this component needs to function.
  */
 protected function componentDataDefinition()
 {
     $component_data_definition = array('module_root_name' => array('label' => 'Module machine name', 'default' => 'my_module', 'required' => TRUE), 'module_readable_name' => array('label' => 'Module readable name', 'default' => function ($component_data) {
         return ucwords(str_replace('_', ' ', $component_data['module_root_name']));
     }, 'required' => FALSE), 'module_short_description' => array('label' => 'Module .info file description', 'default' => 'TODO: Description of module', 'required' => FALSE), 'module_package' => array('label' => 'Module .info file package', 'default' => NULL, 'required' => FALSE), 'module_dependencies' => array('label' => 'Module dependencies (space separated list)', 'default' => NULL, 'required' => FALSE), 'module_help_text' => array('label' => 'Module help text (adds hook_help())', 'default' => NULL, 'required' => FALSE), 'module_hook_presets' => array('label' => 'Hook preset groups', 'required' => FALSE, 'format' => 'array', 'options' => function (&$property_info) {
         /// ARGH how to make format that is good for both UI and drush?
         $mb_factory = module_builder_get_factory('ModuleBuilderEnvironmentDrush');
         $mb_task_handler_report_presets = $mb_factory->getTask('ReportHookPresets');
         $hook_presets = $mb_task_handler_report_presets->getHookPresets();
         // Stash the hook presets in the property info so the processing
         // callback doesn't have to repeat the work.
         $property_info['_presets'] = $hook_presets;
         $options = array();
         foreach ($hook_presets as $name => $info) {
             $options[$name] = $info['label'];
         }
         return $options;
     }, 'processing' => function ($value, &$component_data, &$property_info) {
         // Get the presets from where the 'options' callback left them.
         $hook_presets = $property_info['_presets'];
         foreach ($value as $given_preset_name) {
             if (!isset($hook_presets[$given_preset_name])) {
                 throw new \ModuleBuilderException("Undefined hook preset group {$given_preset_name}.");
             }
             // DX: check the preset is properly defined.
             if (!is_array($hook_presets[$given_preset_name]['hooks'])) {
                 throw new \ModuleBuilderException("Incorrectly defined hook preset group {$given_preset_name}.");
             }
             // Add the preset hooks list to the hooks array in the component
             // data.
             $hooks = $hook_presets[$given_preset_name]['hooks'];
             $component_data['hooks'] = array_merge($component_data['hooks'], $hooks);
             drush_print_r($component_data['hooks']);
         }
     }), 'hooks' => array('label' => 'Hook implementations', 'required' => FALSE, 'format' => 'array', 'options' => function (&$property_info) {
         $mb_factory = module_builder_get_factory('ModuleBuilderEnvironmentDrush');
         $mb_task_handler_report_hooks = $mb_factory->getTask('ReportHookData');
         $hook_options = $mb_task_handler_report_hooks->listHookNamesOptions();
         return $hook_options;
     }, 'processing' => function ($value, &$component_data, &$property_info) {
         $mb_factory = module_builder_get_factory('ModuleBuilderEnvironmentDrush');
         $mb_task_handler_report_hooks = $mb_factory->getTask('ReportHookData');
         // Get the flat list of hooks, standardized to lower case.
         $hook_definitions = array_change_key_case($mb_task_handler_report_hooks->getHookDeclarations());
         $hooks = array();
         foreach ($component_data['hooks'] as $hook_name) {
             // Standardize to lowercase.
             $hook_name = strtolower($hook_name);
             // By default, accept the short definition of hooks, ie 'boot' for 'hook_boot'.
             if (isset($hook_definitions["hook_{$hook_name}"])) {
                 $hooks["hook_{$hook_name}"] = TRUE;
             } elseif (isset($hook_definitions[$hook_name])) {
                 $hooks[$hook_name] = TRUE;
             }
         }
         $component_data['hooks'] = $hooks;
     }), 'plugins' => array('label' => 'Plugins', 'required' => FALSE, 'format' => 'array', 'options' => function (&$property_info) {
         $mb_factory = module_builder_get_factory('ModuleBuilderEnvironmentDrush');
         $mb_task_handler_report_plugins = $mb_factory->getTask('ReportPluginData');
         $options = $mb_task_handler_report_plugins->listPluginNamesOptions();
         return $options;
     }, 'component' => 'Plugin'), 'settings_form' => array('label' => "Admin settings form", 'required' => FALSE, 'format' => 'boolean', 'component' => 'AdminSettingsForm'), 'router_items' => array('label' => "required router paths, eg 'path/foo'", 'required' => FALSE, 'format' => 'array', 'component' => 'RouterItem'), 'module_camel_case_name' => array('computed' => TRUE, 'default' => function ($component_data) {
         $pieces = explode('_', $component_data['module_root_name']);
         $pieces = array_map('ucfirst', $pieces);
         return implode('', $pieces);
     }));
     return $component_data_definition;
 }
Example #8
0
 /**
  * Return a file footer.
  */
 function code_footer()
 {
     $footer = module_builder_get_factory()->environment->getSetting('module_builder_footer', '');
     return $footer;
 }