$node) { $nodes[$key]->machine_name = defaultcontent_get_default($node->nid); } } /** * Implements of hook_node_delete(). * * Node that the machine_name does not have a node now but we keep it around so * we know not to load the default */ function defaultcontent_node_delete($node) { if (isset($node->machine_name)) { defaultcontent_set_default($node->machine_name, NULL); } } /** * Implements hook_node_insert(). * * Record machine name */ function defaultcontent_node_insert($node) { if (isset($node->machine_name)) { defaultcontent_set_default($node->machine_name, $node->nid); } } /** * Implements hook_node_update(). */ function defaultcontent_node_update($node) { defaultcontent_node_insert($node); } /** * Implements hook_form_alter(). * * Add machine name to node form * TODO: get the machine_name type to work */ function defaultcontent_form_alter(&$form, &$form_state, $form_id) { if (user_access('edit node machine name') && strpos($form_id, 'node_form')) { $form['machine_name_fs'] = array( '#type' => 'fieldset', '#title' => 'Machine Name', '#group' => 'additional_settings', ); $form['machine_name_fs']['machine_name'] = array( '#type' => 'machine_name', '#type' => 'textfield', '#title' => 'Machine Name', '#required' => FALSE, '#description' => 'Provide a unique name for this node', '#default_value' => isset($form_state['node']->machine_name) ? $form_state['node']->machine_name : '', ); } } /** * Implements hook_menu(). * * We are implementing the same functions as a node page but with a machine * name url * * NOTE: this was mainly done before we had the menu plugin and now is most * likely not used this page can cause issue for breadcrumbs that is why this * method was abandoned */ function defaultcontent_menu() { $items = array(); $items['node-name/%'] = array( 'page callback' => 'defaultcontent_page_view', 'page arguments' => array(1), 'access callback' => TRUE ); return $items; } /** * Load up node */ function defaultcontent_page_view($name) { $node = defaultcontent_get_node($name); if ($node) { $_GET['q'] = 'node/' . $node->nid; return menu_execute_active_handler('node/' . $node->nid, FALSE); } drupal_not_found() ; } /** * Implements hook_permission(). */ function defaultcontent_permission() { return array('edit node machine name' => array( 'title' => t('Edit node machine name'), 'description' => t('Allow user to edit the machine name associated with a node.'), )); } /** * Implements hook_cron(). * * we check to see if there are any defaults * that have not been load in the db. if we have any we load them */ function defaultcontent_cron($allow_first_content = TRUE) { if ($allow_first_content) { variable_set('node_content_enabled', TRUE); } // I have to load the freature files // I am thinking there should be a way to have features do this but I do not // know what it would be $features = features_get_features(); foreach ($features as $f_name => $feature) { if (isset($feature->info['features']['content'])) { module_load_include('inc', $f_name, $f_name . '.features.content'); } if (isset($feature->info['features']['content_menu_links'])) { module_load_include('inc', $f_name, $f_name . '.features.content_menu_links'); } } // features_include_defaults(); $defaults = module_invoke_all('content_defaults'); usort($defaults, 'defaultcontent_import_sort'); $records = defaultcontent_get_default(); $current = array(); foreach ($records as $record) { $current[] = $record->name; } foreach ($defaults as $default) { // we check to see if that default has been loaded (even if the node // has been deleted) before we do a load if (!in_array($default->machine_name, $current)) { defaultcontent_import_node($default); } } //Turns out if this isn't called only once after EACH install then we get ourselves in trouble - so //we'll just run one time for each change - Major issue with this approach is that install->uninstalling and reinstalling an app will result in no menu item //added //Yes, its hacky, sorry :( $default_menu_links = module_invoke_all('content_menu_links_defaults'); $hashes = variable_get('defaultcontent_hashes', array()); //arrays are value copied when assigned in php - so this is safe $inverse = $hashes; foreach ($default_menu_links as $key => $default) { $str = implode(array_keys($default), ','); $hash = md5($key . $str); $run = variable_get('defaultcontent_run_' . $hash, FALSE); if (!$run) { $try = defaultcontent_import_menu_link($key, $default); if ($try) { variable_set('defaultcontent_run_' . $hash, TRUE); array_push($hashes, $hash); } } $key = array_search($hash, $inverse); unset($inverse[$key]); } foreach ($inverse as $i => $hash) { variable_del('defaultcontent_run_' . $hash); unset($hashes[$i]); } $hashes = array_values($hashes); variable_set('defaultcontent_hashes', $hashes); } /** * Used to set a default * * @param $name * The machine name * @param $nid * The nid of the node */ function defaultcontent_set_default($name, $nid = NULL) { $current_nid = defaultcontent_get_default($name); if ($nid === FALSE) { db_delete('defaultcontent')->condition('name', $name)->execute(); } if ($current_nid === $nid) { // nothing to change here return; } $record = (object) array('name' => $name, 'nid' => $nid); $primary_keys = $current_nid === FALSE ? array() :array('name'); drupal_write_record('defaultcontent', $record, $primary_keys); } /** * Used to get a machine name or nid * * @param $id * An int $nid or a string machine_name * * @return unknown_type * an nid a machine name are an array of machine_name=>nid */ function defaultcontent_get_default($id = FALSE) { if (!$id) { return db_select('defaultcontent', 'd') ->fields('d', array('name', 'nid')) ->execute(); } $have = is_numeric($id) ? 'nid' : 'name'; $want = is_numeric($id) ? 'name' : 'nid'; $values = db_select('defaultcontent', 'd') ->condition($have, $id) ->fields('d', array($want)) ->execute() ->fetchCol(); return !empty($values) ? $values[0] : FALSE; } /** * Helper function to get a node when pass a machine name * * TODO: i have to keep a static temp because for some reason node_load returns * 0 the second time around * * @param $name * A machine name * * @return object * Node object */ function defaultcontent_get_node($name) { static $temp; if (is_numeric(defaultcontent_get_default($name))) { $nid = defaultcontent_get_default($name); if (!isset($temp[$nid])) { $temp[$nid] = node_load($nid); } return $temp[$nid]; } } /** * This function turns a node in to code that can be used in a * hook_defaultcontents */ function defaultcontent_export_node($node) { $node = defaultcontent_export_node_process($node); return '(object) ' . features_var_export($node, ' '); } /** * Produces an export object form a node object */ function defaultcontent_export_node_process($node) { $plugins = defaultcontent_get_alter_plugins('export_alter'); $export = (object) array(); foreach ($plugins as $plugin) { $cb = isset($plugin['export_alter callback']) ? $plugin['export_alter callback'] : $plugin['name'] . '_export_alter'; if (function_exists($cb)) { $cb($node, $export); } } return $export; } /** * This function takes a node object from hook_defaultcontents and insert it * into the db * TODO: need to do something fun with files * TODO: need to do something fun with node refs */ function defaultcontent_import_node($node) { $plugins = defaultcontent_get_alter_plugins(); foreach ($plugins as $plugin) { $cb = isset($plugin['import_alter callback']) ? $plugin['import_alter callback'] : $plugin['name'] . '_import_alter'; if (function_exists($cb)) { $cb($node); } } if (variable_get('node_content_enabled', FALSE)) { node_save($node); } foreach ($plugins as $plugin) { $cb = isset($plugin['post_import callback']) ? $plugin['post_import callback'] : $plugin['name'] . '_post_import'; if (function_exists($cb)) { $cb($node); } } } /** * imports a new menu item */ function defaultcontent_import_menu_link($key, $link) { module_load_include('inc', 'features', 'includes/features.menu'); if (variable_get('node_content_enabled', FALSE)) { $key = defaultcontent_alter_identifier($key, FALSE); $existing = features_menu_link_load($key); if ($existing) { $link['mlid'] = $existing['mlid']; } else { $link['mlid'] = 0; } if (isset($link['parent_path'])) { $link['parent_path'] = defaultcontent_alter_path($link['parent_path'], 0); } $link['link_path'] = defaultcontent_alter_path($link['link_path'], 0); $menu_links_prime[$key] = $link; if (!empty($link['parent_path']) && $parent = features_menu_link_load("{$link['menu_name']}:{$link['parent_path']}")) { $link['plid'] = $parent['mlid']; } else { $link['plid'] = 0; } if ($mid = menu_link_save($link)) { return TRUE; } } return FALSE; } /** * use to sort component * * this ask all alter plugins for a import_sort callback */ function defaultcontent_import_sort($a, $b) { $plugins = defaultcontent_get_alter_plugins(); $cmp = 0; foreach ($plugins as $plugin) { $cb = isset($plugin['import_sort callback']) ? $plugin['import_sort callback'] : $plugin['name'] . '_import_sort'; if (function_exists($cb)) { $cmp = $cb($a, $b); } } return $cmp; } /** * Implementats hook_features_api(). * * we define the node_content type of export */ function defaultcontent_features_api() { return array( 'content' => array( 'name' => t('Content Item'), 'default_hook' => 'content_defaults', 'default_file' => FEATURES_DEFAULTS_INCLUDED, 'features_source' => TRUE, 'file' => drupal_get_path('module', 'defaultcontent') . '/defaultcontent.features.inc', ), 'content_menu_links' => array( 'name' => t('Content Menu links'), 'default_hook' => 'content_menu_links_defaults', 'default_file' => FEATURES_DEFAULTS_INCLUDED, 'features_source' => TRUE, 'file' => drupal_get_path('module', 'defaultcontent') . '/defaultcontent.features.inc', ), ); } /** * Implements hook_features_pipe_COMPONENT_alter() for the 'content' component. */ function defaultcontent_features_pipe_content_alter(&$pipe, $data, $export) { // Ensure that this module is listed as a dependency. if (empty($pipe['dependencies']) || !in_array($pipe['dependencies'], 'defaultcontent')) { $pipe['dependencies'][] = 'defaultcontent'; } } /** * Implements hook_ctools_plugin_type(). */ function defaultcontent_ctools_plugin_type() { return array( 'alter' => array( 'defaults' => array( 'export_alter weight' => 0, ), ) ); } /** * Return all alter plugins * * @param $use * A plugin key that can be used for sorting "$use weight" will be used to * sort the plugins */ function defaultcontent_get_alter_plugins($use = FALSE) { ctools_include('plugins'); $plugins = ctools_get_plugins('defaultcontent', 'alter'); // check if the plugin has a enabled callback if so make sure this plugin // is enabled foreach ($plugins as $id => $plugin) { $cb = isset($plugin['enabled callback']) ? $plugin['enabled callback'] : $plugin['name'] . '_enabled'; if (function_exists($cb)) { if (!$cb()) { unset($plugins[$id]); } } } if ($use) { usort($plugins, create_function('$a, $b', "return \$a['$use weight'] - \$b['$use weight'];")); } return $plugins; } /** * Implements hook_ctools_plugin_directory(). */ function defaultcontent_ctools_plugin_directory($module, $plugin) { if ($module == 'defaultcontent' && $plugin == 'alter') { return 'plugins'; } } /**** Node Blocks *****/ /* * we are making it so that context uses the machine name of the node * instead of the nid for storing the delta of the block * this way when it is exported it can find the content on import */ /** * Implements hook_context_block_info_alter(). * * we change all nodeblock to use a machine name when load into context */ function defaultcontent_context_block_info_alter(&$blocks) { foreach ($blocks as $key => $block) { if ($block->module == 'nodeblock' && is_numeric($block->delta) && ($machine_name = defaultcontent_get_default($block->delta))) { $block->delta = $machine_name; $block->bid = "nodeblock-$machine_name"; $blocks["nodeblock-$machine_name"] = $block; unset($blocks[$key]); } } } /** * Implements hook_context_plugins(). * * Provide a condition based on the machine name. */ function defaultcontent_context_plugins() { $plugins = array(); $plugins['defaultcontent_condition'] = array( 'handler' => array( 'path' => drupal_get_path('module', 'defaultcontent') .'/context/plugins', 'file' => 'defaultcontent_condition.inc', 'class' => 'defaultcontent_condition', 'parent' => 'context_condition', ), ); return $plugins; } /* * Implements hook_context_registry(). */ function defaultcontent_context_registry() { return array( 'conditions' => array( 'defaultcontent' => array( 'title' => t('Machine Name'), 'description' => 'Set Context based on a node\'s machine name. Provide one machine name per line.', 'plugin' => 'defaultcontent_condition', ), ), ); } /* * Implements hook_node_view(). */ function defaultcontent_node_view($node, $view_mode, $langcode) { if (module_exists('context') && $plugin = context_get_plugin('condition', 'defaultcontent')) { $plugin->execute($node); } } /** * Implements hook_block_view_alter(). * * we turn all nodeblock blocks that use machine name to use nid */ function defaultcontent_block_view_alter(&$data, $block) { if ($block->module == 'nodeblock' && !is_numeric($block->delta) && ($nid = defaultcontent_get_default($block->delta))) { $data = nodeblock_block_view($nid); } } /** * Implements hook_url_outbound_alter(). * * changing the outbound path to the node path */ function defaultcontent_url_outbound_alter_DISABLED(&$path, &$options, $original_path) { if (preg_match('|node-name/(.*)|', path, $matches) && ($nid = defaultcontent_get_default($matches[1]) ) ) { $path = drupal_get_path_alias('node/' . $nid); } } /** * Implements hook_preprocess_link(). * * we are going to preorcess the link function so we can make sure we set it to * active even if we are using the node-name path */ function defaultcontent_preprocess_link_DISABLED(&$vars) { if (preg_match('|node-name/(.*)|', $vars['path'], $matches) && ($nid = defaultcontent_get_default($matches[1]))) { if ($_GET['q'] == 'node/' . $nid) { $vars['options']['attributes']['class'][] = 'active'; } } } /** * Implements hook_modules_disabled(). * * we want to remove default content on disable, but only content that has not * been overridden */ function defaultcontent_modules_disabled($modules) { foreach ($modules as $module) { defaultcontent_remove_default_content($module); } } /** * Implements hook_modules_enable(). * * run our cron to insure that all of the new content is turned on */ function defaultcontent_modules_enabled($modules) { defaultcontent_cron(FALSE); } /** * Removes all nodes that we created from the db */ // TODO remove menu items function defaultcontent_remove_default_content($module_name) { module_load_include('inc', $module_name, $module_name . '.features.content'); $components = module_invoke($module_name, 'content_defaults'); if (!empty($components)) { usort($components, 'defaultcontent_import_sort'); foreach (array_reverse($components) as $component) { if ($nid = defaultcontent_get_default($component->machine_name)) { $node = node_load($nid, NULL, TRUE); $node = defaultcontent_export_node_process($node); //check if the node has change before we blow it away //and reimport //For the moment we are going to remove it regardless of its overriden status if (TRUE || $node == $component) { //we have to reset this cache or the node goes not get found by node_delete entity_get_controller('node')->resetCache(); node_delete($nid); defaultcontent_set_default($component->machine_name, FALSE); } } } } return TRUE; } function defaultcontent_alter_identifier($identifier, $to_name = TRUE) { list($menu, $path) = explode(':', $identifier); $identifier = $menu . ':' . defaultcontent_alter_path($path, $to_name); return $identifier; } function defaultcontent_alter_path($path, $to_name = TRUE) { $parts = explode('/', $path); if ($to_name) { if (($parts[0] == 'node') && is_numeric($parts[1]) && ($name = defaultcontent_get_default($parts[1]))) { $parts[0] = 'node-name'; $parts[1] = $name; } } elseif (($parts[0] == 'node-name') && !is_numeric($parts[1]) && ($nid = defaultcontent_get_default($parts[1]))) { $parts[0] = 'node'; $parts[1] = $nid; } return implode('/', $parts); }