Beispiel #1
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.
     $directory = \ModuleBuilder\Factory::getEnvironment()->getHooksDirectory();
     // 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 = \ModuleBuilder\Factory::getEnvironment()->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;
 }
 /**
  * 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_task_handler_report_plugins = \ModuleBuilder\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()
 {
     // Sanity checks already done at this point; no need to catch exception.
     $mb_task_handler_report = \ModuleBuilder\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');
 }
 /**
  * Gather hook documentation files.
  *
  * This retrieves a list of api hook documentation files from drupal.org's
  * version control server.
  */
 protected function gatherHookDocumentationFiles()
 {
     $directory = \ModuleBuilder\Factory::getEnvironment()->getHooksDirectory();
     // Fetch data about the files we need to download.
     $hook_files = $this->getHookFileUrls($directory);
     //print_r($hook_files);
     // Retrieve each file and store it in the hooks directory, overwriting what's currently there
     foreach ($hook_files as $file_name => $data) {
         $file_contents = drupal_http_request($data['url']);
         // TODO: replace with call to environment output.
         //_module_builder_drush_print("writing $directory/$file_name", 2);
         file_put_contents("{$directory}/{$file_name}", $destination . $file_contents->data);
     }
     return $hook_files;
 }
Beispiel #5
0
 /**
  * Gather hook documentation files.
  *
  * This retrieves a list of api hook documentation files from drupal.org's
  * version control server.
  */
 protected function gatherHookDocumentationFiles()
 {
     $directory = \ModuleBuilder\Factory::getEnvironment()->getHooksDirectory();
     // Fetch data about the files we need to download.
     $hook_files = $this->getHookFileUrls($directory);
     //print_r($hook_files);
     // For testing only: skip downloading, just process.
     /*
     module_builder_process_hook_data($hook_files);
     return $hook_files;
     */
     // Retrieve each file and store it in the hooks directory, overwriting what's currently there
     foreach ($hook_files as $file_name => $data) {
         $file_contents = drupal_http_request($data['url']);
         // TODO: replace with call to environment output.
         //_module_builder_drush_print("writing $directory/$file_name", 2);
         file_put_contents("{$directory}/{$file_name}", $destination . $file_contents->data);
     }
     // inform that hook documentation has been downloaded.
     drupal_set_message(t("Module Builder has just downloaded hook documentation to your %dir directory from CVS. This documentation contains detailed descriptions and usage examples of each of Drupal's hooks. Please view the files for more information, or view them online at the <a href=\"!api\">Drupal API documentation</a> site.", array('%dir' => 'files/' . variable_get('module_builder_hooks_directory', 'hooks'), '!api' => url('http://api.drupal.org/'))));
     return $hook_files;
 }
Beispiel #6
0
    /**
     * Return the main body of the file code.
     */
    function code_body()
    {
        $component_data = $this->getRootComponentData();
        // Sanity checks already done at this point; no need to catch exception.
        $mb_task_handler_analyze = \ModuleBuilder\Factory::getTask('AnalyzeModule');
        $hooks = $mb_task_handler_analyze->getInventedHooks($component_data['root_name']);
        $module_root_name = $component_data['root_name'];
        $module_root_name_title_case = ucfirst($component_data['root_name']);
        $module_readable_name = $component_data['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;
    }
 /**
  * Define the component data this component needs to function.
  */
 protected function componentDataDefinition()
 {
     $component_data_definition = array('root_name' => array('label' => 'Module machine name', 'default' => 'my_module', 'required' => TRUE), 'readable_name' => array('label' => 'Module readable name', 'default' => function ($component_data) {
         return ucwords(str_replace('_', ' ', $component_data['root_name']));
     }, 'required' => FALSE), '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', 'description' => 'The machine names of the modules this module should have as dependencies.', 'default' => array(), 'required' => FALSE, 'format' => 'array'), 'module_help_text' => array('label' => 'Module help text', 'description' => 'The text to show on the site help page. This automatically adds hook_help().', 'default' => NULL, 'required' => FALSE), 'settings_form' => array('label' => "Admin settings form", 'required' => FALSE, 'format' => 'boolean', 'component' => 'AdminSettingsForm'), 'services' => array('label' => "Services", 'description' => 'A list of machine names of services for this module to provide.', 'required' => FALSE, 'format' => 'array', 'component' => 'Service'), 'permissions' => array('label' => "Permissions", 'description' => 'A list of machine names of permissions for this module to provide.', 'required' => FALSE, 'format' => 'array', 'component' => 'Permissions'), '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_task_handler_report_presets = \ModuleBuilder\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 \ModuleBuilder\Exception\InvalidInputException(strtr("Undefined hook preset group !name", array('!name' => htmlspecialchars($given_preset_name, ENT_QUOTES, 'UTF-8'))));
             }
             // DX: check the preset is properly defined.
             if (!is_array($hook_presets[$given_preset_name]['hooks'])) {
                 throw new \ModuleBuilder\Exception\InvalidInputException(strtr("Incorrectly defined hook preset group !name", array('!name' => htmlspecialchars($given_preset_name, ENT_QUOTES, 'UTF-8'))));
             }
             // 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_task_handler_report_hooks = \ModuleBuilder\Factory::getTask('ReportHookData');
         $hook_options = $mb_task_handler_report_hooks->listHookNamesOptions();
         return $hook_options;
     }, 'options_structured' => function (&$property_info) {
         $mb_task_handler_report_hooks = \ModuleBuilder\Factory::getTask('ReportHookData');
         $hook_options = $mb_task_handler_report_hooks->listHookOptionsStructured();
         return $hook_options;
     }, 'processing' => function ($value, &$component_data, &$property_info) {
         $mb_task_handler_report_hooks = \ModuleBuilder\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_task_handler_report_plugins = \ModuleBuilder\Factory::getTask('ReportPluginData');
         $options = $mb_task_handler_report_plugins->listPluginNamesOptions();
         return $options;
     }, 'component' => 'Plugin'), 'router_items' => array('label' => "required router paths, eg 'path/foo'", 'required' => FALSE, 'format' => 'array', 'component' => 'RouterItem'), 'camel_case_name' => array('computed' => TRUE, 'default' => function ($component_data) {
         $pieces = explode('_', $component_data['root_name']);
         $pieces = array_map('ucfirst', $pieces);
         return implode('', $pieces);
     }));
     return $component_data_definition;
 }
 /**
  * Get hook information declared by Module Builder.
  *
  * This invokes our own hook, hook_module_builder_info(), as well as adding
  * hardcoded info such as module file locations, which can't be deduced from
  * either api.php files or hook_hook_info().
  */
 protected function getHookInfo()
 {
     // Get data by invoking our hook.
     $data = \ModuleBuilder\Factory::getEnvironment()->invokeInfoHook();
     // Add our data.
     $result = $this->getAdditionalHookInfo();
     $data = array_merge($data, $result);
     return $data;
 }
 /**
  * Return a file footer.
  */
 function code_footer()
 {
     $footer = \ModuleBuilder\Factory::getEnvironment()->getSetting('footer', '');
     return $footer;
 }
Beispiel #10
0
 /**
  * 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 the Collect task 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)
 {
     // Sanity checks already done at this point; no need to catch exception.
     $mb_task_handler_report = \ModuleBuilder\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.
     // 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 = \ModuleBuilder\Factory::getEnvironment()->getCoreMajorVersion();
     $template_base_path_module = \ModuleBuilder\Factory::getEnvironment()->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 = $this->parseTemplate($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;
 }