outline_designer.module in Outline Designer 7
Same filename and directory in other branches
Massive usability improvement for quickly structuring / creating content.
This lets you build out content quickly while obeying permissions.
File
outline_designer.moduleView source
<?php
// ELMS: Outline Designer - Usability improvements for speedy outline creation in Drupal 6.x
// Copyright (C) 2008-2012 The Pennsylvania State University
//
// Bryan Ollendyke
// bto108@psu.edu
//
// Keith D. Bailey
// kdb163@psu.edu
//
// 12 Borland
// University Park, PA 16802
/**
* @file
* Massive usability improvement for quickly structuring / creating content.
*
* This lets you build out content quickly while obeying permissions.
*/
/**
* Implements hook_help().
*/
function outline_designer_help($path, $arg) {
switch ($path) {
case 'admin/content/book/settings':
return 'Use this to change what content types are available while outlining content. The default will also be what is automatically selected when adding new content.';
case 'admin/content/book/outline_designer':
return "Use icons that visually describe your content so that there is no confusion about what's being created. It also helps in visualizing your site when you have pages, containers, webforms and other content all living in the same hierarchy.";
case 'admin/structure/block/add':
}
}
/**
* Implements hook_init().
*/
function outline_designer_init() {
if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'book' && (arg(3) == '' || arg(3) == 'list')) {
drupal_add_css(drupal_get_path('module', 'outline_designer') . '/css/outline_designer.css');
$js = '
function od_add_book(){
window.location="' . base_path() . '?q=node/add/' . variable_get('book_child_type', 'book') . '";
}
$(document).ready(function(){$(".sticky-table").before(' . "'" . '<div style="width:120px;" class="context-menu context-menu-theme-' . variable_get('outline_designer_theme', 'vista') . '"><div title="" class="context-menu-item"><div style="background-image: url(' . base_path() . drupal_get_path('module', 'outline_designer') . '/images/add_content.png);" class="context-menu-item-inner" onclick="od_add_book();">' . t('add book') . '</div></div></div>' . "');\n });";
drupal_add_js($js, array(
'type' => 'inline',
'scope' => JS_DEFAULT,
));
}
}
function _outline_designer_setup($nid, $ajax_path) {
global $user;
drupal_add_css(drupal_get_path('module', 'outline_designer') . '/css/outline_designer.css');
drupal_add_js(drupal_get_path('module', 'outline_designer') . '/script/jquery.contextmenu.js');
drupal_add_js(drupal_get_path('module', 'outline_designer') . '/script/overrides.js', array(
'type' => 'header',
'scope' => JS_DEFAULT,
));
drupal_add_js(drupal_get_path('module', 'outline_designer') . '/script/scripts.js', array(
'type' => 'footer',
'scope' => JS_DEFAULT,
));
// Select only those that the book module say can be outlined
$types_ary = variable_get('book_allowed_types', array(
'page',
));
// Make sure default is allowed by this user.
if (node_access('create', variable_get('book_child_type', 'book'))) {
$default_type = variable_get('book_child_type', 'book');
}
else {
foreach ($types_ary as $current_type) {
if (node_access('create', $current_type)) {
$default_type = $current_type;
}
}
}
// create the array of menu items unavailable to the user by combining
// the unavailable menu items in common in all of the user's roles
$unchecked_menu_items = array();
$view_all = FALSE;
if ($user->uid == 1) {
// user1 should always see all menu items
$view_all = TRUE;
}
else {
$saved_unchecked_items = variable_get('outline_designer_context_menu_exclusion_matrix', array());
// collect only the items unchecked in all of the user's roles
foreach (array_keys($user->roles) as $rid) {
if ($saved_unchecked_items[$rid] != NULL) {
$unchecked_menu_items = array_keys($saved_unchecked_items[$rid]);
if (isset($tmp)) {
$unchecked_menu_items = array_intersect($tmp, $unchecked_menu_items);
}
$tmp = $unchecked_menu_items;
}
else {
// because we're storing all the roles that are exceptions to the rule, if they have a role that isn't in the list, they automatically have access to all buttons in the context menu
$view_all = TRUE;
}
}
}
// the permission check found that this user should have access to view everything so ignore the unchecked metric
if ($view_all) {
$unchecked_menu_items = array();
}
$purl = '';
if (module_exists('purl')) {
$options = array();
// need to look for a group if it exists otherwise it should work fine
if (arg(0) == 'admin') {
$node = node_load($nid);
purl_url_outbound_alter($purl, $options, '');
$ajax_base_path = base_path() . $purl . $ajax_path;
$node = node_load($nid);
if (count($node->og_groups)) {
$group = node_load(array_pop($node->og_groups));
$ajax_base_path = base_path() . $group->purl . '/' . $ajax_path;
}
else {
$ajax_base_path = base_path() . $ajax_path;
}
}
else {
purl_url_outbound_alter($purl, $options, '');
$ajax_base_path = base_path() . $purl . $ajax_path;
}
}
else {
$ajax_base_path = base_path() . $ajax_path;
}
// pass variables to js
$js_variables = array(
'path' => base_path() . drupal_get_path('module', 'outline_designer') . '/',
'types' => array(),
'theme' => variable_get('outline_designer_theme', 'vista'),
'unavailableContextMenuItems' => array_values($unchecked_menu_items),
'collapseToggle' => variable_get('outline_designer_collapse_toggle', 1),
'collapseList' => array(),
'token' => drupal_get_token(),
'factor' => 1,
'activeNid' => '',
'rootNid' => $nid,
'defaultType' => $default_type,
'activeType' => $default_type,
'ajaxPath' => $ajax_base_path,
);
$result = db_query("SELECT type,name FROM {node_type} ORDER BY name");
while ($value = db_fetch_array($result)) {
// ensure there is no permission escalation
if (array_search($value['type'], $types_ary) === FALSE) {
}
elseif (node_access('create', $value['type'])) {
$js_variables['types'][$value['type']] = array(
$value['name'],
variable_get("outline_designer_" . $value['type'] . "_icon", drupal_get_path('module', 'outline_designer') . "/images/page.png"),
);
}
}
drupal_add_js(array(
'outline_designer' => $js_variables,
), array(
'type' => "setting",
'scope' => JS_DEFAULT,
));
// dual purpose of the function, since we calcuated this array, return it in-case we need to hide the add button
return array_values($unchecked_menu_items);
}
/**
* Implements hook_form_alter().
*/
function outline_designer_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'book_admin_edit' && (arg(0) == "admin" || arg(0) == 'outline_designer' && arg(1) == 'ajax')) {
_outline_designer_book_admin_form_alter($form, $form_state, $form_id, 'outline_designer/ajax/');
}
}
function _outline_designer_book_admin_form_alter(&$form, $form_state, $form_id, $ajax_path) {
$icon_path = base_path() . drupal_get_path('module', 'outline_designer') . '/images/';
$unavailableContextMenuItems = _outline_designer_setup($form['#node']->nid, $ajax_path);
$form['ahah'] = array(
'#type' => 'submit',
'#id' => 'reload_table',
'#value' => t('AHAH submit'),
'#weight' => 1,
'#submit' => array(),
'#ahah' => array(
'event' => 'change',
'path' => $ajax_path . drupal_get_token() . '/reload_table/' . $form['#node']->nid,
'wrapper' => 'od-book-edit',
'method' => 'replace',
'effect' => 'none',
'progress' => array(
'type' => 'throbber',
'message' => t('Loading..'),
),
),
);
$od_path = drupal_get_path('module', 'outline_designer');
// unset things so that the form renders with everything stripped off
// helps OD work in browsers that respect <enter> button for submitting the whole page/form
$form["#submit"] = '';
$form["#action"] = 'outline_designer/ajax/' . drupal_get_token() . '/reload_table/' . $form['#node']->nid;
$form["#method"] = 'POST';
$form["save"] = '';
$form["#suffix"] = outline_designer_suffix();
$base_path = base_path();
$count = 0;
foreach ($form['table'] as $key => $row) {
$count++;
if ($key != '#theme' && $key != '#tree') {
$tmpnid = $row['nid']['#value'];
$type = db_query("SELECT type FROM {node} WHERE nid = :nid", array(
':nid' => $tmpnid,
))
->fetchField();
$form['table'][$key]['title']['#prefix'] = '<img alt="' . $type . '" id="node-' . $tmpnid . '-icon" src="' . $base_path . variable_get("outline_designer_" . $type . "_icon", $od_path . "/images/page.png") . '" alt="Nid: ' . $tmpnid . '" title="Nid: ' . $tmpnid . '" style="float:left; margin:2px;" /><img src="' . $icon_path . 'settings.png" alt="' . t('Content operations') . '" title="' . t('Content operations') . '" id="node-' . $tmpnid . '" class="outline_designer_edit_button" />';
// if this has kids then give it a drop down
if ($form['table'][$key]['#item']['has_children'] == 1) {
$form['table'][$key]['title']['#suffix'] = '<img src="' . $icon_path . 'open.png" alt="open" class="od-toggle-open" id="collapse-' . $tmpnid . '" />';
}
}
}
// this is to trap for a goofy Drupal js error that's core
if ($count == 2) {
// Select only those that the book module say can be outlined
$types_ary = variable_get('book_allowed_types', array(
'page',
));
// make sure default is allowed by this user
if (node_access('create', variable_get('book_child_type', 'book'))) {
$default_type = variable_get('book_child_type', 'book');
}
else {
foreach ($types_ary as $current_type) {
if (node_access('create', $current_type)) {
$default_type = $current_type;
}
}
}
drupal_set_message(t('Books need to have at least one piece of content in them in order to work correctly with the outline designer'), 'error');
drupal_goto('node/add/' . $default_type, array(
'query' => array(
'parent' => '\\\' . $form[\\\'#node\\\']->book[\\\'mlid\\\'] . \\\'',
'destination' => '\\\' . $_GET[\\\'q\\\']',
),
));
}
$form['table']['#prefix'] = '
<fieldset class="collapsible">
<div>';
// if the add content button is removed from their context menu it needs to be removed here too
if (array_search('add_content', $unavailableContextMenuItems) === FALSE) {
$form['table']['#prefix'] .= '<div style="width:120px; float:left; height:30px; margin-right:6px;" class="context-menu context-menu-theme-' . variable_get('outline_designer_theme', 'vista') . '"><div title="Add Content" alt="Add Content" class="context-menu-item">
<div style="background-image: url(' . $icon_path . 'add_content.png);" class="context-menu-item-inner" onclick="Drupal.settings.outline_designer.activeNid=' . $form['#node']->nid . ';Drupal.outline_designer.form_render(' . "'add_content'" . ');">
' . t('Add content') . '
</div>
</div>
</div>';
}
$form['table']['#prefix'] .= '<div style="height:30px; float:left;">
<a href="#" onclick="Drupal.outline_designer.scale(-1);" class="od-operation-icons">
<img alt="' . t('Decrease interface') . '" title="' . t('Decrease interface') . '" src="' . $icon_path . 'text-decrease.png" />
</a>
<a href="#" onclick="Drupal.outline_designer.scale(1);" class="od-operation-icons">
<img alt="' . t('Increase interface') . '" title="' . t('Increase interface') . '" src="' . $icon_path . 'text-increase.png" />
</a>
<a href="#" onclick="Drupal.outline_designer.scale(0);" class="od-operation-icons">
<img alt="' . t('Reset interface') . '" title="' . t('Reset interface') . '" src="' . $icon_path . 'text-reset.png" />
</a>
<a href="#" onclick="Drupal.outline_designer.openAll();" class="od-operation-icons">
<img alt="' . t('Open all branches') . '" title="' . t('Open all branches') . '" src="' . $icon_path . 'open-all.png" />
</a>
<a href="#" onclick="Drupal.outline_designer.collapseAll();" class="od-operation-icons">
<img alt="' . t('Close all branches') . '" title="' . t('Close all branches') . '" src="' . $icon_path . 'close-all.png" />
</a>
</div>
</div>
</fieldset>';
}
/**
* Implements hook_menu().
*/
function outline_designer_menu() {
$items = array();
$items['outline_designer/ajax'] = array(
'access arguments' => array(
'administer book outlines',
),
'page callback' => '_outline_designer_ajax',
'page arguments' => array(
2,
3,
4,
5,
6,
),
'type' => MENU_CALLBACK,
);
$items['admin/content/book/outline_designer'] = array(
'title' => 'Usability',
'description' => 'The Outline Designer settings allow you to associate icons to content types for use in structuring book content.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'_outline_designer_settings',
),
'type' => MENU_LOCAL_TASK,
'weight' => 9,
'access arguments' => array(
'administer site configuration',
),
);
$items['admin/config/outline_designer'] = array(
'title' => 'Outline Designer',
'description' => 'The Outline Designer settings allow you to associate icons to content types for use in structuring book content.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'_outline_designer_settings_redirect',
),
'type' => MENU_NORMAL_ITEM,
'access arguments' => array(
'administer site configuration',
),
);
return $items;
}
function _outline_designer_settings_redirect() {
drupal_goto('admin/content/book/outline_designer');
}
/**
* Implementation of hook_settings().
*/
function _outline_designer_settings($form_state) {
// build out a list of the packaged icons
$icons = array(
'add_content',
'change_type',
'close',
'delete',
'duplicate',
'edit',
'folder',
'link',
'node',
'page',
'rename',
'save',
'settings',
'story',
'view',
);
$packaged_icons = '<label>' . t('Default Icon set') . ':</label>';
foreach ($icons as $title) {
$packaged_icons .= '<img src="' . base_path() . drupal_get_path('module', 'outline_designer') . '/images/' . $title . '.png" title="' . $title . '" alt="' . $title . '" hspace="2px" />';
}
// add in archived icon set on a separate row
$packaged_icons .= '<br/><label>' . t('Additional icon') . ':</label>';
foreach ($icons as $title) {
$packaged_icons .= '<img src="' . base_path() . drupal_get_path('module', 'outline_designer') . '/images/additional_icons/' . $title . '.png" title="' . $title . '" alt="' . $title . '" hspace="2px" />';
}
// create it juuuust incase and make sure it's writable
$dir = file_create_path(file_directory_path() . '/outline_designer');
file_prepare_directory($dir, 1);
if ($handle = opendir('./' . file_directory_path() . '/outline_designer')) {
while (FALSE !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$uploaded_icons .= '<img src="' . base_path() . file_directory_path() . '/outline_designer/' . $file . '" title="' . $file . '" alt="' . $file . '" hspace="2px" />';
}
}
closedir($handle);
}
// collapse default state
$form["ui"] = array(
'#type' => 'fieldset',
'#title' => t('User interface'),
'#collapsed' => FALSE,
'#collapsible' => TRUE,
);
$form["ui"]["outline_designer_theme"] = array(
'#type' => 'select',
'#title' => t('Menu theme'),
'#default_value' => variable_get('outline_designer_theme', 'vista'),
'#options' => array(
'vista' => 'Vista',
'xp' => 'XP',
'human' => 'Human',
'osx' => 'OS X',
),
'#description' => 'Theme for the outline designer menu system',
'#required' => TRUE,
);
// collapse default state
$form["ui"]["outline_designer_collapse_toggle"] = array(
'#type' => 'checkbox',
'#title' => t('Collapse nested content by default?'),
'#default_value' => variable_get('outline_designer_collapse_toggle', 1),
'#description' => 'This is helpful when working with large book structures.',
'#required' => FALSE,
);
// context menu settings
$form["context_menu"] = array(
'#type' => 'fieldset',
'#title' => t('Context menu'),
'#collapsed' => FALSE,
'#collapsible' => TRUE,
'#description' => 'The selected items will appear in the Outline Designer context menu, depending on the user\'s roles. Users with several roles will cumulate the roles settings.<br />WARNING: only roles with permission \'administer book outlines\' or group admins (if outline_designer_og is activated) will be able to utilize these settings.',
'#theme' => 'outline_designer_context_menu_items_matrix',
);
$roles = user_roles(TRUE);
$menu_items = array(
'nid' => t('Node id'),
'add_content' => t('Add content'),
'rename' => t('Rename'),
'edit' => t('Edit'),
'view' => t('View'),
'delete' => t('Delete'),
'duplicate' => t('Duplicate'),
'change_type' => t('Change Type'),
);
$saved_unchecked_items = variable_get('outline_designer_context_menu_exclusion_matrix', array());
// create a checkbox for each menu item for each role
foreach ($menu_items as $item_name => $item_desc) {
foreach ($roles as $rid => $role) {
// if the checkbox is present in the 'outline_designer_context_menu_exclusion_matrix' variable,
// then we need to uncheck it (e.g. assign FALSE to #default_value)
$default_value = isset($saved_unchecked_items[$rid][$item_name]['unchecked']) ? FALSE : TRUE;
$form["context_menu"]["checkboxes"][$item_name]["outline_designer_context_menu_" . $item_name . "_" . $rid] = array(
'#name' => 'outline_designer_context_menu_' . $item_name . '_' . $rid,
'#type' => 'checkbox',
'#title' => $item_desc,
'#default_value' => $default_value,
'#return_value' => array(
'item_name' => $item_name,
'rid' => $rid,
),
);
}
}
// icons
$form["packaged_icons"] = array(
'#type' => 'fieldset',
'#title' => t('Packaged icons'),
'#collapsed' => FALSE,
'#collapsible' => TRUE,
);
$form["packaged_icons"]["packaged"] = array(
'#type' => 'markup',
'#title' => 'packaged',
'#value' => $packaged_icons,
);
$form["uploaded_icons"] = array(
'#type' => 'fieldset',
'#title' => t('Uploaded Icons'),
'#collapsed' => FALSE,
'#collapsible' => TRUE,
);
$form["uploaded_icons"]["uploaded"] = array(
'#type' => 'markup',
'#value' => $uploaded_icons,
);
$result = db_query("SELECT type, name FROM {node_type}");
$types_ary = variable_get('book_allowed_types', array(
'page',
));
while ($value = db_fetch_array($result)) {
// only show types that are allowed
if (in_array($value['type'], $types_ary)) {
// create a textfield incase they want to enter an icon that way
$form["outline_designer_" . $value['type']] = array(
'#type' => 'fieldset',
'#title' => '<img src="' . base_path() . variable_get("outline_designer_" . $value['type'] . "_icon", drupal_get_path('module', 'outline_designer') . "/images/page.png") . '" style="display:inline !important" /> ' . $value['name'],
'#collapsed' => TRUE,
'#collapsible' => TRUE,
'#description' => t("This icon will be associated to the content type in book outlines. The icon must be 16x16, unless an <a href=@toolkit>image toolkit</a> is installed, and in jpg, gif or png format.", array(
'@toolkit' => url('admin/config/media/image-toolkit'),
)),
);
$form["outline_designer_" . $value['type']]["outline_designer_" . $value['type'] . "_icon_link"] = array(
'#type' => 'textfield',
'#title' => t("Icon Path"),
'#default_value' => variable_get("outline_designer_" . $value['type'] . "_icon", drupal_get_path('module', 'outline_designer') . "/images/page.png"),
'#required' => FALSE,
);
// Create a upload field for each content type so icons can be added for them
$form["outline_designer_" . $value['type']]["outline_designer_" . $value['type'] . "_icon"] = array(
'#type' => 'file',
'#size' => '10',
'#title' => t("Upload Icon"),
'#required' => FALSE,
);
}
}
$form['#attributes'] = array(
'enctype' => "multipart/form-data",
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
/**
* Implementation of hook_settings_submit().
*/
function _outline_designer_settings_submit($form, &$form_state) {
$checkboxes = array();
$items_unchecked = array();
// store the context menu settings
$form_values = array_filter($form_state['values']);
$menu_items = array_keys($form["context_menu"]["checkboxes"]);
// collect all the ticked checkboxes
foreach ($form_values as $key => $val) {
if (strpos($key, "outline_designer_context_menu_") !== FALSE) {
$checkboxes[] = $key;
}
}
// extrapolate which items have unchecked by comparing the list of
// ticked checkboxes with the list of all the context menu checkboxes
foreach ($menu_items as $item) {
if (substr($item, 0, 1) != "#") {
$item_checkboxes = $form["context_menu"]["checkboxes"][$item];
foreach ($item_checkboxes as $cb_name => $cb_properties) {
if ($cb_properties['#type'] == 'checkbox' && !in_array($cb_name, $checkboxes)) {
$items_unchecked[$cb_properties['#return_value']['rid']][$cb_properties['#return_value']['item_name']]['unchecked'] = TRUE;
}
}
}
}
variable_set('outline_designer_context_menu_exclusion_matrix', array_filter($items_unchecked));
// store the checkbox value
variable_set('outline_designer_collapse_toggle', $form_state['values']["outline_designer_collapse_toggle"]);
// store theme selected
variable_set('outline_designer_theme', $form_state['values']["outline_designer_theme"]);
// take into account outline child pages
if (function_exists('outline_child_pages_menu')) {
variable_set('outline_child_pages_type', $form_state['values']["outline_child_pages_type"]);
}
// the rest is for files
$dir = file_create_path(file_directory_path() . '/outline_designer');
$is_writable = file_prepare_directory($dir, 1);
if ($is_writable) {
$validators = array(
'file_validate_is_image' => array(),
'_outline_designer_validate_image_resolution' => array(
'16x16',
),
'file_validate_size' => array(
30 * 1024,
),
);
$result = db_query("SELECT type, name FROM {node_type}");
while ($value = db_fetch_array($result)) {
if ($file = file_save_upload("outline_designer_" . $value['type'] . "_icon", $validators, $dir)) {
drupal_set_message(t('New @title icon saved.', array(
'@title' => $value['name'],
)));
variable_set("outline_designer_" . $value['type'] . "_icon", $file->filepath);
}
else {
// this is the case when there is no image uploaded to associate the textfield icon to the icon page to use, this will allow for references to icons already used
$icon = check_plain($form_state['values']["outline_designer_" . $value['type'] . "_icon_link"]);
$base_path = base_path();
// pull off the site name if it was included
if ($base_path != '/' && $base_path != '') {
if (strpos(' ' . $icon, $base_path) != 0) {
$pos = strpos($icon, $base_path) + drupal_strlen($base_path);
$icon = drupal_substr($icon, $pos);
}
}
// clean up the string incase those other two didn't do the trick
$icon = drupal_substr($icon, strpos($icon, drupal_get_path('module', 'outline_designer')));
$icon = drupal_substr($icon, strpos($icon, file_directory_path()));
// potentially this isn't a valid icon path on our server...need to still check this
variable_set("outline_designer_" . $value['type'] . "_icon", $icon);
}
}
}
drupal_set_message(t('Settings saved'));
}
/**
* Implementation of the ajax menu hook
*
* @args
* $token = user token to block CSF attack
* $action = action that's being performed
* $var1 = typically nid being acted on
* $var2 = additional data like title, ect.
* $var3 = even more data like type
*/
function _outline_designer_ajax($token, $action, $var1, $var2, $var3) {
global $user;
if (drupal_valid_token($token)) {
switch ($action) {
// in: active nid, parent nid, new weight
// action: reorganize nodes
// out: node id, node type, icon to render
case 'drag_drop':
$nid = $var1;
$parent_nid = $var2;
$weight = $var3;
// load the active node
$node = node_load($nid);
// make sure they can update this node
if (node_access('update', $node)) {
// load parent
$parent = node_load($parent_nid);
// set parent
$node->book['plid'] = $parent->book['mlid'];
$node->book['weight'] = $weight;
$node->revision = 1;
$node->log = t("Outline Designer -- nid:%nid parent nid changed to %parent_nid", array(
'%nid' => $nid,
'%parent_nid' => $parent_nid,
));
node_save($node);
print t("Content position has been updated (nid:%nid)", array(
'%nid' => $nid,
));
}
else {
print t("Content position has not been updated due to invalid permissions!");
}
break;
// in: node id, weight
// action: saved / updated node
// out: message
case 'reweight':
$nid = $var1;
$weight = $var2;
$node = node_load($nid);
// make sure they can update this node
if (node_access('update', $node)) {
// set parent / weight
$node->book['weight'] = $weight;
node_save($node);
print t("Content position has been updated (nid:%nid)", array(
'%nid' => $nid,
));
}
else {
print t("Content position has not been updated due to invalid permissions!");
}
break;
// in: nothing
// action: rebuild the HTML of the table
// out: AHAH tabledrag object
case 'reload_table':
$nid = $var1;
module_load_include('inc', 'book', 'book.admin');
$output = drupal_get_form('book_admin_edit', node_load($nid));
drupal_json_output(array(
'status' => TRUE,
'data' => $output,
));
break;
// in: node title, node parent id
// action: Insert a node
// out: parent menu id, and message / new nid
case 'add_content':
$title = $var1;
$type = $var2;
$parent_nid = $var3;
// need to account for the 3 weird characters in URLs
$title = str_replace("@2@F@", '/', $title);
$title = str_replace("@2@3@", '#', $title);
$title = str_replace("@2@B@", '+', $title);
$title = str_replace("@2@6@", '&', $title);
// set the node
$node = new stdClass();
$node->title = $title;
$node->type = $type;
$node->uid = $user->uid;
// load up the parent of this new item and then copy over the book structure stuff
$parent = node_load($parent_nid);
$node->book['weight'] = -15;
$node->book['plid'] = $parent->book['mlid'];
$node->book['bid'] = $parent->book['bid'];
$node->book['menu_name'] = $parent->book['menu_name'];
$node->book['module'] = $parent->book['module'];
// Allow other modules to alter the new book node.
drupal_alter('new_book_object', $node);
if (node_access('create', $node)) {
node_save($node);
watchdog('content', '%type: added %title.', array(
'%type' => $node->type,
'%title' => $node->title,
));
print $parent->book['mlid'] . ';msg:' . t('%type added (nid: %nid)', array(
'%type' => $node->type,
'%nid' => $node->nid,
));
}
else {
print 0;
}
break;
// in: node id, title
// action: rename a single node
// out: nothing
case 'rename':
$nid = $var1;
$newtitle = $var2;
// need to account for the 3 weird characters in URLs
$newtitle = str_replace("@2@F@", '/', $newtitle);
$newtitle = str_replace("@2@3@", '#', $newtitle);
$newtitle = str_replace("@2@B@", '+', $newtitle);
$newtitle = str_replace("@2@6@", '&', $newtitle);
$node = node_load($nid);
if (node_access('update', $node)) {
$node->log = t("Outline Designer -- node renamed from %title to %newtitle", array(
'%title' => $node->title,
'%newtitle' => $newtitle,
));
watchdog('content', t("Outline Designer -- node %nid renamed from %title to %newtitle", array(
'%nid' => $nid,
'%title' => $node->title,
'%newtitle' => $newtitle,
)));
print t("Content renamed from %title to %newtitle", array(
'%title' => $node->title,
'%newtitle' => $newtitle,
));
$node->title = $newtitle;
$node->revision = 1;
node_save($node);
}
else {
print t("You don't have permissions to rename this content");
}
break;
// in: root node id
// action: duplicate the node and associate the tree to the new branch (if multiple)
// out: new root node
case 'duplicate':
$nid = $var1;
$multiple = $var2;
$dup_title = $var3;
// need to account for the 3 weird characters in URLs
$dup_title = str_replace("@2@F@", '/', $dup_title);
$dup_title = str_replace("@2@3@", '#', $dup_title);
$dup_title = str_replace("@2@B@", '+', $dup_title);
// only duplicate 1 item
if ($multiple == 0) {
$node = node_load($nid);
$orig_node = $node;
$node->nid = NULL;
$node->created = NULL;
$node->book['mlid'] = NULL;
$node->book['has_children'] = 0;
$node->uid = $user->uid;
// swap out the title
$new_title = str_replace('@title', $node->title, $dup_title);
$node->title = $new_title;
if (node_access('create', $node) && node_access('view', $orig_node)) {
node_save($node);
print t('Content duplicated from %title (%nid).', array(
'%title' => $node->title,
'%nid' => $nid,
));
watchdog('content', t('%type: duplicated %title from node %nid.', array(
'%type' => $node->type,
'%title' => $node->title,
'%nid' => $nid,
)));
}
else {
print 0;
}
}
else {
$permission_fail = FALSE;
$nids = $nid;
$map = array();
$node = node_load($nid);
// pull only the nodes that have the original node as a parent
$mlid = $node->book['mlid'];
$result = db_query("\n SELECT link_path\n FROM {menu_links}\n WHERE p2=%d OR p3=%d OR p4=%d OR p5=%d OR p6=%d OR p7=%d OR p8=%d OR p9=%d\n ORDER BY depth ASC", $mlid, $mlid, $mlid, $mlid, $mlid, $mlid, $mlid, $mlid);
while ($value = db_fetch_array($result)) {
$dup_node = node_load(str_replace('node/', '', $value['link_path']));
$orig_node = $dup_node;
$current_nid = $dup_node->nid;
$old_nid = $dup_node->nid;
$dup_node->nid = NULL;
$dup_node->created = NULL;
$dup_node->path = NULL;
$dup_node->uid = $user->uid;
$dup_node->revision = 1;
// swap out the title
$dup_node->title = str_replace('@title', $dup_node->title, $dup_title);
$old_mlid = $dup_node->book['mlid'];
$dup_node->book['mlid'] = NULL;
$dup_node->log = t("Outline Designer -- Duplicate of old node nid:%old_nid", array(
'%old_nid' => $old_nid,
));
if (isset($map[$dup_node->book['plid']])) {
$dup_node->book['plid'] = $map[$dup_node->book['plid']];
}
if (node_access('create', $dup_node) && node_access('view', $orig_node)) {
node_save($dup_node);
$map[$old_mlid] = $dup_node->book['mlid'];
watchdog('content', '%type: duplicated %title from node %current_nid.', array(
'%type' => $dup_node->type,
'%title' => $dup_node->title,
'%current_nid' => $current_nid,
));
$nids .= ', ' . $old_nid;
}
else {
$permission_fail = TRUE;
}
}
if ($permission_fail) {
print 0;
}
else {
print t('Content duplicated (nids:%nids)', array(
'%nids' => $nids,
));
}
}
break;
// in: list of node ids to delete
// action: delete nodes
// out: completion response
case 'delete':
$nid = $var1;
$multiple = $var2;
$node = node_load($nid);
if ($multiple == 0) {
if (node_access('delete', $node)) {
print t('Content deleted (nid:%nid)', array(
'%nid' => $nid,
));
node_delete($nid);
}
else {
print 0;
}
}
else {
$node = node_load($nid);
// pull only the nodes that have this node as a parent
$mlid = $node->book['mlid'];
// make sure this isn't used to delete non book nodes
$del_count = 0;
$count = 0;
if ($mlid != 0 && $mlid != '') {
$result = db_query("SELECT link_path FROM {menu_links} WHERE module = :module AND (p1 = :(p1 OR p2 = :p2 OR p3 = :p3 OR p4 = :p4 OR p5 = :p5 OR p6 = :p6 OR p7 = :p7 OR p8 = :p8 OR p9 = :p9)", array(
':module' => 'book',
':(p1' => $mlid,
':p2' => $mlid,
':p3' => $mlid,
':p4' => $mlid,
':p5' => $mlid,
':p6' => $mlid,
':p7' => $mlid,
':p8' => $mlid,
':p9' => $mlid,
));
while ($value = db_fetch_array($result)) {
$count++;
$node = node_load(str_replace('node/', '', $value['link_path']));
if (node_access('delete', $node)) {
$del_count++;
node_delete($node->nid);
}
}
}
if ($del_count == 0) {
print 0;
}
elseif ($count != $del_count) {
print t("A multiple delete was commited but you didn't have the permissions to delete all content so some remain.");
}
else {
print t("All content deleted successfully.");
}
}
break;
// in: node id, node type name
// action: change node type and resave
// out: icon to render
case 'change_type':
$nid = $var1;
$new_type = $var2;
$node = node_load($nid);
if (node_access('update', $node)) {
$node->log = t('Outline Designer -- Content Type changed from %type to %new_type', array(
'%type' => $node->type,
'%new_type' => $new_type,
));
watchdog('content', t('Outline Designer -- Content Type changed from %type to %new_type', array(
'%type' => $node->type,
'%new_type' => $new_type,
)));
print t('Content type changed from %type to %new_type', array(
'%type' => $node->type,
'%new_type' => $new_type,
));
$node->type = $new_type;
node_save($node);
}
else {
print 0;
}
break;
}
}
exit;
}
// used in validating that an icon upload is the correct size
function _outline_designer_validate_image_resolution(&$file, $dimensions) {
$errors = array();
// Check first that the file is an image.
if ($info = image_get_info($file->filepath)) {
// Check if the icon matches the given dimensions.
list($width, $height) = explode('x', $dimensions);
if ($info['width'] != $width || $info['height'] != $height) {
// Try to resize the image to fit the dimensions if it doesn't.
if (image_get_toolkit() && image_scale_and_crop($file->filepath, $file->filepath, $width, $height)) {
drupal_set_message(t('The image was resized to the allowed dimensions of %dimensions pixels.', array(
'%dimensions' => $dimensions,
)));
// Clear the cached filesize and refresh the image information.
clearstatcache();
$info = image_get_info($file->filepath);
$file->filesize = $info['file_size'];
}
else {
$errors[] = t('Image dimensions need to be %dimensions pixels.', array(
'%dimensions' => $maximum_dimensions,
));
}
}
}
return $errors;
}
/**
* Suffix adds the info to tak on the edn of the form
*/
function outline_designer_suffix() {
// Select only those that the book module say can be outlined
$types_ary = variable_get('book_allowed_types', array(
'page',
));
// make sure the user can submit these types via permissions
$result = db_query("SELECT type,name FROM {node_type} ORDER BY name");
$typeoutput = '<table><tr>';
$count = 0;
while ($value = db_fetch_array($result)) {
// ensure there is no permission escalation in type switching
if (array_search($value['type'], $types_ary) === FALSE) {
}
elseif (node_access('create', $value['type'])) {
$count++;
$typeoutput .= '<td><input type="radio" class="type_radio" name="content_type[]" value="' . $value['type'] . '"/> <img src="' . base_path() . variable_get("outline_designer_" . $value['type'] . "_icon", drupal_get_path('module', 'outline_designer') . "/images/page.png") . '" />' . $value['name'] . '</td>';
if ($count % 3 == 0) {
$typeoutput .= '</tr><tr>';
}
}
}
$typeoutput .= '</tr></table>';
$duplicate_form['od_duplicate_number'] = array(
'#title' => t('How many copies would you like?'),
'#type' => 'select',
'#id' => 'od_duplicate_number',
'#description' => t('Duplicates will be performed one at a time to avoid load issues'),
'#options' => array(
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
),
'#required' => TRUE,
'#default_value' => 1,
'#weight' => 0,
);
$duplicate_form['od_duplicate_multiple'] = array(
'#title' => t('Duplicate Hierarchy'),
'#id' => 'od_duplicate_multiple',
'#type' => 'checkbox',
'#attributes' => array(
'checked' => 'checked',
),
'#description' => t('If checked, the current content and all child pages will be duplicated'),
'#weight' => 1,
);
$duplicate_form['od_duplicate_title'] = array(
'#title' => t('Title format'),
'#id' => 'od_duplicate_title',
'#type' => 'textfield',
'#required' => TRUE,
'#size' => 20,
'#description' => t('Tokens: @title = content title; @i = duplicate number'),
'#weight' => 2,
);
// delete form
$delete_form['od_delete_multiple'] = array(
'#title' => t('Delete Hierarchy'),
'#id' => 'od_delete_multiple',
'#type' => 'checkbox',
'#description' => t('Warning: This action cannot be undone.'),
'#weight' => 0,
);
// add form
$add_form['od_add_content_title'] = array(
'#title' => t('Title'),
'#id' => 'od_add_content_title',
'#type' => 'textfield',
'#required' => TRUE,
'#size' => 20,
'#description' => t('Make sure you edit content after creation if it has "required fields"
'),
'#weight' => 0,
'#suffix' => $typeoutput,
);
return '
<div id="od_popup_overlay"></div>
<div id="od_popup" class="context-menu context-menu-theme-' . variable_get('outline_designer_theme', 'vista') . '">
<div class="popup-statusbar"></div>
<div class="popup-content"></div>
<div class="popup-buttons">
<input type="button" name="Save" value="Save" class="od_submit_button" />
</div>
<img src="' . base_path() . drupal_get_path('module', 'outline_designer') . '/images/close.png" class="od_cancel_button" />
</div>
<div id="od_popup_toolbox">
<div id="od_duplicate" class="od_uiscreen">
' . drupal_render($duplicate_form) . '
</div>
<div id="od_delete" class="od_uiscreen">
' . drupal_render($delete_form) . '
</div>
<div id="od_change_type" class="od_uiscreen">
<div class="description">' . t('Warning: Changing content types can have undesirable effects') . '</div>
' . $typeoutput . '
</div>
<div id="od_add_content" class="od_uiscreen">
' . drupal_render($add_form) . '
</div>
</div>';
}
/**
* Implements hook_theme().
*/
function outline_designer_theme() {
$theme = array(
'outline_designer_context_menu_items_matrix' => array(
'render element' => 'form',
),
);
return $theme;
}
/**
* Display the Context menu items as a matrix (columns=user roles, rows=menu items)
*/
function theme_outline_designer_context_menu_items_matrix($variables) {
$form = $variables['form'];
$roles = user_roles(TRUE);
foreach (element_children($form["checkboxes"]) as $item_name) {
$trow = array();
foreach ($roles as $rid => $role) {
$trow[] = drupal_render_children($form["checkboxes"][$item_name]["outline_designer_context_menu_" . $item_name . "_" . $rid]);
}
$rows[] = $trow;
}
$output = theme('table', array(
'header' => array_values($roles),
'rows' => $rows,
));
return $output;
}
Functions
Name![]() |
Description |
---|---|
outline_designer_form_alter | Implements hook_form_alter(). |
outline_designer_help | Implements hook_help(). |
outline_designer_init | Implements hook_init(). |
outline_designer_menu | Implements hook_menu(). |
outline_designer_suffix | Suffix adds the info to tak on the edn of the form |
outline_designer_theme | Implements hook_theme(). |
theme_outline_designer_context_menu_items_matrix | Display the Context menu items as a matrix (columns=user roles, rows=menu items) |
_outline_designer_ajax | Implementation of the ajax menu hook |
_outline_designer_book_admin_form_alter | |
_outline_designer_settings | Implementation of hook_settings(). |
_outline_designer_settings_redirect | |
_outline_designer_settings_submit | Implementation of hook_settings_submit(). |
_outline_designer_setup | |
_outline_designer_validate_image_resolution |