/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }