[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/ctools/page_manager/ -> page_manager.admin.inc (source)

   1  <?php
   2  // $Id: page_manager.admin.inc,v 1.27.2.18 2010/10/29 22:40:52 merlinofchaos Exp $
   3  
   4  /**
   5   * @file
   6   * Administrative functions for the page manager.
   7   *
   8   * This provides the UI to list, create, edit and delete pages, though much
   9   * of this is delegated through to individual tasks.
  10   */
  11  
  12  /**
  13   * Output a list of pages that are managed.
  14   */
  15  function page_manager_list_page($js = NULL) {
  16    // Prevent this page from showing up when random other links fail.
  17    if ($js && $js != 'ajax' && $js != 'nojs') {
  18      return drupal_not_found();
  19    }
  20  
  21    // TRUE if 'ajax', FALSE if otherwise.
  22    $js = $js == 'ajax';
  23  
  24    if (module_exists('advanced_help') && !$js) {
  25      drupal_set_message(theme('advanced_help_topic', 'page_manager', 'getting-started', t('See the getting started guide for more information.')));
  26    }
  27  
  28    $tasks = page_manager_get_tasks_by_type('page');
  29    $pages = array('operations' => array(), 'tasks' => array());
  30  
  31    page_manager_get_pages($tasks, $pages);
  32  
  33    // Add lock icon to all locked tasks.
  34    global $user;
  35    ctools_include('object-cache');
  36    $locks = ctools_object_cache_test_objects('page_manager_page', $pages['tasks']);
  37    foreach ($locks as $task_name => $lock) {
  38      if ($lock->uid == $user->uid) {
  39        $pages['rows'][$task_name]['class'] .= ' page-manager-locked';
  40        $pages['rows'][$task_name]['title'] = t('This page is currently locked for editing by you. Nobody else may edit this page until these changes are saved or canceled.');
  41      }
  42      else {
  43        $pages['rows'][$task_name]['class'] .= ' page-manager-locked-other';
  44        $pages['rows'][$task_name]['title'] = t('This page is currently locked for editing by another user. You may not edit this page without breaking the lock.');
  45      }
  46    }
  47  
  48    $input = $_POST;
  49  
  50    // Respond to a reset command by clearing session and doing a drupal goto
  51    // back to the base URL.
  52    if (isset($input['op']) && $input['op'] == t('Reset')) {
  53      unset($_SESSION['page_manager']['#admin']);
  54      if (!$js) {
  55        return drupal_goto($_GET['q']);
  56      }
  57      // clear everything but form id, form build id and form token:
  58      $keys = array_keys($input);
  59      foreach ($keys as $id) {
  60        if ($id != 'form_id' && $id != 'form_build_id' && $id != 'form_token') {
  61          unset($input[$id]);
  62        }
  63      }
  64      $replace_form = TRUE;
  65    }
  66    if (count($input) <= 1) {
  67      if (isset($_SESSION['page_manager']['#admin']) && is_array($_SESSION['page_manager']['#admin'])) {
  68        $input  = $_SESSION['page_manager']['#admin'];
  69      }
  70    }
  71    else {
  72      $_SESSION['page_manager']['#admin'] = $input;
  73      unset($_SESSION['page_manager']['#admin']['q']);
  74    }
  75  
  76    $form_state = array(
  77      'pages' => &$pages,
  78      'input' => $input,
  79      'rerender' => TRUE,
  80      'no_redirect' => TRUE,
  81    );
  82  
  83    // This form will sort and filter the pages.
  84    ctools_include('form');
  85    $form = ctools_build_form('page_manager_list_pages_form', $form_state);
  86  
  87    $header = array(
  88      array('data' => t('Type'), 'class' => 'page-manager-page-type'),
  89      array('data' => t('Name'), 'class' => 'page-manager-page-name'),
  90      array('data' => t('Title'), 'class' => 'page-manager-page-title'),
  91      array('data' => t('Path'), 'class' => 'page-manager-page-path'),
  92      array('data' => t('Storage'), 'class' => 'page-manager-page-storage'),
  93    );
  94  
  95    $header[] = array('data' => t('Operations'), 'class' => 'page-manager-page-operations');
  96    $table = theme('table', $header, $pages['rows'], array('id' => 'page-manager-list-pages'));
  97  
  98    $operations = '<div id="page-manager-links" class="links">' . theme('links', $pages['operations']) . '</div>';
  99  
 100    drupal_add_css(drupal_get_path('module', 'page_manager') . '/css/page-manager.css');
 101  
 102    if (!$js) {
 103      return $form . $table . $operations;
 104    }
 105  
 106    ctools_include('ajax');
 107    $commands = array();
 108    $commands[] = ctools_ajax_command_replace('#page-manager-list-pages', $table);
 109    if (!empty($replace_form)) {
 110      $commands[] = ctools_ajax_command_replace('#page-manager-list-pages-form', $form);
 111    }
 112    ctools_ajax_render($commands);
 113  }
 114  
 115  /**
 116   * Sort tasks into buckets based upon whether or not they have subtasks.
 117   */
 118  function page_manager_get_pages($tasks, &$pages, $task_id = NULL) {
 119    foreach ($tasks as $id => $task) {
 120      if (empty($task_id) && !empty($task['page operations'])) {
 121        $pages['operations'] = array_merge($pages['operations'], $task['page operations']);
 122      }
 123  
 124      // If a type has subtasks, add its subtasks in its own table.
 125      if (!empty($task['subtasks'])) {
 126        page_manager_get_pages(page_manager_get_task_subtasks($task), $pages, $task['name']);
 127        continue;
 128      }
 129  
 130      if (isset($task_id)) {
 131        $task_name = page_manager_make_task_name($task_id, $task['name']);
 132      }
 133      else {
 134        $task_name = $task['name'];
 135      }
 136  
 137      $class = 'page-task-' . $id;
 138      if (isset($task['row class'])) {
 139        $class .= ' ' . $task['row class'];
 140      }
 141  
 142      if (!empty($task['disabled'])) {
 143        $class .= ' page-manager-disabled';
 144      }
 145  
 146      $path = array();
 147      $visible_path = '';
 148      if (!empty($task['admin path'])) {
 149        foreach (explode('/', $task['admin path']) as $bit) {
 150          if ($bit[0] != '!') {
 151            $path[] = $bit;
 152          }
 153        }
 154  
 155        $path = implode('/', $path);
 156        if (empty($task['disabled']) && strpos($path, '%') === FALSE) {
 157          $visible_path = l('/' . $task['admin path'], $path);
 158        }
 159        else {
 160          $visible_path = '/' . $task['admin path'];
 161        }
 162      }
 163  
 164      $row = array('data' => array(), 'class' => $class, 'title' => strip_tags($task['admin description']));
 165  
 166      $type = isset($task['admin type']) ? $task['admin type'] : t('System');
 167      $pages['types'][$type] = $type;
 168      $row['data']['type'] = array('data' => $type, 'class' => 'page-manager-page-type');
 169  
 170      $row['data']['name'] = array('data' => $task_name, 'class' => 'page-manager-page-name');
 171      $row['data']['title'] = array('data' => $task['admin title'], 'class' => 'page-manager-page-title');
 172      $row['data']['path'] = array('data' => $visible_path, 'class' => 'page-manager-page-path');
 173  
 174      $storage = isset($task['storage']) ? $task['storage'] : t('In code');
 175      $pages['storages'][$storage] = $storage;
 176      $row['data']['storage'] = array('data' => $storage, 'class' => 'page-manager-page-storage');
 177  
 178  
 179  /*
 180      if (empty($task['disabled'])) {
 181        $item = menu_get_item($path);
 182        if (empty($item)) {
 183          dsm($path);
 184        }
 185        else {
 186          dsm($item);
 187        }
 188      }
 189  */
 190      $operations = array(
 191        array(
 192          'title' => t('Edit'),
 193          'href' => page_manager_edit_url($task_name),
 194        ),
 195      );
 196  
 197      if (!empty($task['enable callback'])) {
 198        if (!empty($task['disabled'])) {
 199          $operations[] = array(
 200            'title' => t('Enable'),
 201            'href' => 'admin/build/pages/nojs/enable/' . $task_name,
 202            'query' => array('token' => drupal_get_token($task_name)),
 203          );
 204        }
 205        else {
 206          $operations[] = array(
 207            'title' => t('Disable'),
 208            'href' => 'admin/build/pages/nojs/disable/' . $task_name,
 209            'query' => array('token' => drupal_get_token($task_name)),
 210          );
 211        }
 212      }
 213  
 214      $row['data']['operations'] = array('data' => theme('links', $operations), 'class' => 'page-manager-page-operations');
 215  
 216      $pages['disabled'][$task_name] = !empty($task['disabled']);
 217      $pages['tasks'][] = $task_name;
 218      $pages['rows'][$task_name] = $row;
 219    }
 220  }
 221  
 222  /**
 223   * Provide a form for sorting and filtering the list of pages.
 224   */
 225  function page_manager_list_pages_form(&$form_state) {
 226    // This forces the form to *always* treat as submitted which is
 227    // necessary to make it work.
 228    if (empty($_POST)) {
 229      $form["#programmed"] = TRUE;
 230    }
 231    $form['#action'] = url('admin/build/pages/nojs/', array('absolute' => TRUE));
 232    if (!variable_get('clean_url', FALSE)) {
 233      $form['q'] = array(
 234        '#type' => 'hidden',
 235        '#value' => $_GET['q'],
 236      );
 237    }
 238  
 239    $all = array('all' => t('<All>'));
 240  
 241    $form['type'] = array(
 242      '#type' => 'select',
 243      '#title' => t('Type'),
 244      '#options' => $all + $form_state['pages']['types'],
 245      '#default_value' => 'all',
 246    );
 247  
 248    $form['storage'] = array(
 249      '#type' => 'select',
 250      '#title' => t('Storage'),
 251      '#options' => $all + $form_state['pages']['storages'],
 252      '#default_value' => 'all',
 253    );
 254  
 255    $form['disabled'] = array(
 256      '#type' => 'select',
 257      '#title' => t('Enabled'),
 258      '#options' => $all + array('0' => t('Enabled'), '1' => t('Disabled')),
 259      '#default_value' => 'all',
 260    );
 261  
 262    $form['search'] = array(
 263      '#type' => 'textfield',
 264      '#title' => t('Search'),
 265    );
 266  
 267    $form['order'] = array(
 268      '#type' => 'select',
 269      '#title' => t('Sort by'),
 270      '#options' => array(
 271        'disabled' => t('Enabled, title'),
 272        'title' => t('Title'),
 273        'name' => t('Name'),
 274        'path' => t('Path'),
 275        'type' => t('Type'),
 276        'storage' => t('Storage'),
 277      ),
 278      '#default_value' => 'disabled',
 279    );
 280  
 281    $form['sort'] = array(
 282      '#type' => 'select',
 283      '#title' => t('Order'),
 284      '#options' => array(
 285        'asc' => t('Up'),
 286        'desc' => t('Down'),
 287      ),
 288      '#default_value' => 'asc',
 289    );
 290  
 291    $form['submit'] = array(
 292      '#name' => '', // so it won't in the $_GET args
 293      '#type' => 'submit',
 294      '#id' => 'edit-pages-apply',
 295      '#value' => t('Apply'),
 296      '#attributes' => array('class' => 'ctools-use-ajax'),
 297    );
 298  
 299    $form['reset'] = array(
 300      '#type' => 'submit',
 301      '#id' => 'edit-pages-reset',
 302      '#value' => t('Reset'),
 303      '#attributes' => array('class' => 'ctools-use-ajax'),
 304    );
 305  
 306    ctools_add_js('ajax-responder');
 307    drupal_add_js('misc/jquery.form.js');
 308    drupal_add_js(drupal_get_path('module', 'page_manager') . '/js/page-list.js');
 309    $form['#theme'] = array('page_manager_list_pages_form');
 310    return $form;
 311  }
 312  
 313  /**
 314   * Accept submission from the page manager sort/filter form and apply it
 315   * to the list of pages.
 316   */
 317  function page_manager_list_pages_form_submit(&$form, &$form_state) {
 318    // Filter and re-sort the pages.
 319  
 320    // This is a copy.
 321    $rows = $form_state['pages']['rows'];
 322  
 323    $sorts = array();
 324    foreach ($rows as $name => $data) {
 325      // Filter
 326      if ($form_state['values']['type'] != 'all' && $form_state['values']['type'] != $data['data']['type']['data']) {
 327        continue;
 328      }
 329  
 330      if ($form_state['values']['storage'] != 'all' && $form_state['values']['storage'] != $data['data']['storage']['data']) {
 331        continue;
 332      }
 333  
 334      if ($form_state['values']['disabled'] != 'all' && $form_state['values']['disabled'] != $form_state['pages']['disabled'][$name]) {
 335        continue;
 336      }
 337  
 338      if ($form_state['values']['search'] &&
 339          strpos($data['data']['name']['data'], $form_state['values']['search']) === FALSE &&
 340          strpos($data['data']['path']['data'], $form_state['values']['search']) === FALSE &&
 341          strpos($data['data']['title']['data'], $form_state['values']['search']) === FALSE) {
 342            continue;
 343      }
 344      // Set up sorting
 345      switch ($form_state['values']['order']) {
 346        case 'disabled':
 347          $sorts[$name] = !$form_state['pages']['disabled'][$name] . $data['data']['title']['data'];
 348          break;
 349        case 'title':
 350          $sorts[$name] = $data['data']['title']['data'];
 351          break;
 352        case 'name':
 353          $sorts[$name] = $data['data']['name']['data'];
 354          break;
 355        case 'path':
 356          $sorts[$name] = $data['data']['path']['data'];
 357          break;
 358        case 'type':
 359          $sorts[$name] = $data['data']['type']['data'];
 360          break;
 361        case 'storage':
 362          $sorts[$name] = $data['data']['storage']['data'];
 363          break;
 364      }
 365    }
 366  
 367    // Now actually sort
 368    if ($form_state['values']['sort'] == 'desc') {
 369      arsort($sorts);
 370    }
 371    else {
 372      asort($sorts);
 373    }
 374  
 375    // Nuke the original.
 376    $form_state['pages']['rows'] = array();
 377    // And restore.
 378    foreach ($sorts as $name => $title) {
 379      $form_state['pages']['rows'][$name] = $rows[$name];
 380    }
 381  
 382  }
 383  
 384  /**
 385   * Render the edit page for a a page, custom or system.
 386   */
 387  function page_manager_edit_page($page) {
 388    ctools_include('form');
 389    drupal_set_title($page->subtask['admin title']);
 390    // Provide and process the save page form before anything else.
 391    $form_state = array('page' => &$page);
 392    $form = ctools_build_form('page_manager_save_page_form', $form_state);
 393  
 394    $operations = page_manager_get_operations($page);
 395    $args = array('summary');
 396    $rendered_operations = page_manager_render_operations($page, $operations, $args, array('class' => 'operations-main'), 'nav');
 397    $content = page_manager_get_operation_content(FALSE, $page, $args, $operations);
 398  
 399    $output = theme('page_manager_edit_page', $page, $form, $rendered_operations, $content);
 400    return $output;
 401  }
 402  
 403  /**
 404   * Entry point to edit a single operation for a page.
 405   *
 406   * @param $js
 407   *   Whether or not the page was called via javascript.
 408   * @param $page
 409   *   The cached page that is being edited.
 410   * @param ...
 411   *   A number of items used to drill down into the actual operation called.
 412   */
 413  function page_manager_edit_page_operation() {
 414    $args = func_get_args();
 415    $js = array_shift($args);
 416    $page = array_shift($args);
 417  
 418    $operations = page_manager_get_operations($page);
 419    $content = page_manager_get_operation_content($js, $page, $args, $operations);
 420  
 421    // If the operation requested we go somewhere else afterward, oblige it.
 422    if (isset($content['new trail'])) {
 423      $args = $content['new trail'];
 424      // Get operations again, for the operation may have changed their availability.
 425      $operations = page_manager_get_operations($page);
 426      $content = page_manager_get_operation_content($js, $page, $args, $operations);
 427    }
 428  
 429    // Rendering the content may have been a form submission that changed the
 430    // operations, such as renaming or adding a handler. Thus we get a new set
 431    // of operations.
 432    $operations = page_manager_get_operations($page);
 433    $rendered_operations = page_manager_render_operations($page, $operations, $args, array('class' => 'operations-main'), 'nav');
 434  
 435    // Since this form should never be submitted to this page, process it late so
 436    // that we can be sure it notices changes.
 437    ctools_include('form');
 438    $form_state = array('page' => &$page);
 439    $form = ctools_build_form('page_manager_save_page_form', $form_state);
 440  
 441    $output = theme('page_manager_edit_page', $page, $form, $rendered_operations, $content);
 442  
 443    if ($js) {
 444      $commands = array();
 445      if (isset($content['js settings'])) {
 446        $commands[] = ctools_ajax_command_settings($content['js settings']);
 447      }
 448      $commands[] = ctools_ajax_command_replace('#page-manager-edit', $output);
 449  
 450      ctools_ajax_render($commands);
 451    }
 452  
 453    drupal_set_title($page->subtask['admin title']);
 454    return $output;
 455  }
 456  
 457  /**
 458   * Take the operations array from a task and expand it.
 459   *
 460   * This allows some of the operations to be dynamic, based upon settings
 461   * on the task or the task's handlers. Each operation should have a type. In
 462   * addition to all the types allowed in page_manager_render_operations, these
 463   * types will be dynamically replaced with something else:
 464   * - 'handlers': An automatically created group that contains all the task's
 465   *   handlers and appropriate links.
 466   * - 'function': A callback (which will be placed in the 'function' parameter
 467   *   that should return an array of operations. This can be used to provide
 468   *   additional, dynamic links if needed.
 469   */
 470  function page_manager_get_operations($page, $operations = NULL) {
 471    if (!isset($operations)) {
 472      // All tasks have at least these 2 ops:
 473      $operations = array(
 474        'summary' => array(
 475          'title' => t('Summary'),
 476          'description' => t('Get a summary of the information about this page.'),
 477          'path' => 'admin/build/pages/edit',
 478          'ajax' => FALSE,
 479          'no operations' => TRUE,
 480          'form info' => array(
 481            'no buttons' => TRUE,
 482          ),
 483          'form' => 'page_manager_page_summary',
 484        ),
 485        'actions' => array(
 486          'type' => 'group',
 487          'title' => '',
 488          'class' => 'operations-actions',
 489          'location' => 'primary',
 490          'children' => array(),
 491        ),
 492      );
 493  
 494      if (isset($page->subtask['operations'])) {
 495        $operations += $page->subtask['operations'];
 496        // add actions separately.
 497        if (!empty($page->subtask['operations']['actions'])) {
 498          $operations['actions']['children'] += $page->subtask['operations']['actions']['children'];
 499        }
 500      }
 501      $operations['handlers'] = array('type' => 'handlers');
 502    }
 503  
 504    $result = array();
 505    foreach ($operations as $id => $operation) {
 506      if (empty($operation['type'])) {
 507        $operation['type'] = 'operation';
 508      }
 509      switch ($operation['type']) {
 510        case 'handlers':
 511          $result[$id] = page_manager_get_handler_operations($page);
 512          break;
 513        case 'function':
 514          if (function_exists($operation['function'])) {
 515            $retval = $function($page, $operation);
 516            if (is_array($retval)) {
 517              $result[$id] = $retval;
 518            }
 519          }
 520          break;
 521        default:
 522          $result[$id] = $operation;
 523      }
 524    }
 525  
 526    if (!empty($page->subtask['enable callback']) && !empty($page->subtask['disabled']) && empty($result['actions']['children']['enable'])) {
 527      $result['actions']['children']['enable'] = array(
 528        'title' => t('Enable'),
 529        'description' => t('Activate this page so that it will be in use in your system.'),
 530        'form' => 'page_manager_enable_form',
 531        'ajax' => FALSE,
 532        'silent' => TRUE,
 533        'no update and save' => TRUE,
 534        'form info' => array(
 535          'finish text' => t('Enable'),
 536        ),
 537      );
 538    }
 539  
 540    if (!empty($page->subtask['enable callback']) && empty($page->subtask['disabled']) && empty($result['actions']['children']['disable'])) {
 541      $result['actions']['children']['disable'] = array(
 542        'title' => t('Disable'),
 543        'description' => t('De-activate this page. The data will remain but the page will not be in use on your system.'),
 544        'form' => 'page_manager_disable_form',
 545        'ajax' => FALSE,
 546        'silent' => TRUE,
 547        'no update and save' => TRUE,
 548        'form info' => array(
 549          'finish text' => t('Disable'),
 550        ),
 551      );
 552    }
 553  
 554    $result['actions']['children']['add'] = array(
 555      'title' => t('Add variant'),
 556      'description' => t('Add a new variant to this page.'),
 557      'form' => 'page_manager_handler_add',
 558      'ajax' => FALSE,
 559      'silent' => TRUE, // prevents a message about updating and prevents this item from showing as changed.
 560      'no update and save' => TRUE, // get rid of update and save button which is bad here.
 561      'form info' => array(
 562        'finish text' => t('Create variant'),
 563      ),
 564    );
 565  
 566    // Restrict variant import to users who can already execute arbitrary PHP
 567    if (user_access('use PHP for block visibility')) {
 568      $result['actions']['children']['import'] = array(
 569        'title' => t('Import variant'),
 570        'description' => t('Add a new variant to this page from code exported from another page.'),
 571        'form' => 'page_manager_handler_import',
 572      );
 573    }
 574  
 575    if (count($page->handlers) > 1) {
 576      $result['actions']['children']['rearrange'] = array(
 577        'title' => t('Reorder variants'),
 578        'ajax' => FALSE,
 579        'description' => t('Change the priority of the variants to ensure that the right one gets selected.'),
 580        'form' => 'page_manager_handler_rearrange',
 581      );
 582    }
 583  
 584    // This is a special operation used to configure a new task handler before
 585    // it is added.
 586    if (isset($page->new_handler)) {
 587    $plugin = page_manager_get_task_handler($page->new_handler->handler);
 588      $result['actions']['children']['configure'] = array(
 589        'title' => t('Configure'),
 590        'description' => t('Configure a newly created variant prior to actually adding it to the page.'),
 591        'ajax' => FALSE,
 592        'no update and save' => TRUE, // get rid of update and save button which is bad here.
 593        'form info' => array(
 594          // We use our own cancel and finish callback to handle the fun stuff.
 595          'finish callback' => 'page_manager_handler_add_finish',
 596          'cancel callback' => 'page_manager_handler_add_cancel',
 597          'show trail' => TRUE,
 598          'show back' => TRUE,
 599          'finish text' => t('Create variant'),
 600        ),
 601        'form' => array(
 602          'forms' => $plugin['forms'],
 603        ),
 604      );
 605  
 606      foreach ($page->forms as $id) {
 607        if (isset($plugin['add features'][$id])) {
 608          $result['actions']['children']['configure']['form']['order'][$id] = $plugin['add features'][$id];
 609        }
 610        else if (isset($plugin['required forms'][$id])) {
 611          $result['actions']['children']['configure']['form']['order'][$id] = $plugin['required forms'][$id];
 612        }
 613      }
 614    }
 615  
 616    if ($page->locked) {
 617      $result['actions']['children']['break-lock'] = array(
 618        'title' => t('Break lock'),
 619        'description' => t('Break the lock on this page so that you can edit it.'),
 620        'form' => 'page_manager_break_lock',
 621        'ajax' => FALSE,
 622        'no update and save' => TRUE, // get rid of update and save button which is bad here.
 623        'form info' => array(
 624          'finish text' => t('Break lock'),
 625         ),
 626        'even locked' => TRUE, // show button even if locked
 627        'silent' => TRUE,
 628      );
 629    }
 630  
 631    drupal_alter('page_manager_operations', $result, $page);
 632    return $result;
 633  }
 634  
 635  /**
 636   * Collect all the operations related to task handlers (variants) and
 637   * build a menu.
 638   */
 639  function page_manager_get_handler_operations(&$page) {
 640    ctools_include('export');
 641    $group = array(
 642      'type' => 'group',
 643      'class' => 'operations-handlers',
 644      'title' => t('Variants'),
 645    );
 646  
 647    $operations = array();
 648  
 649    // If there is only one variant, let's not have it collapsible.
 650    $collapsible = count($page->handler_info) != 1;
 651    foreach ($page->handler_info as $id => $info) {
 652      if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
 653        continue;
 654      }
 655      $handler = $page->handlers[$id];
 656      $plugin = page_manager_get_task_handler($handler->handler);
 657  
 658      $operations[$id] = array(
 659        'type' => 'group',
 660        'class' => 'operations-handlers-' . $id,
 661        'title' => page_manager_get_handler_title($plugin, $handler, $page->task, $page->subtask_id),
 662        'collapsible' => $collapsible,
 663        'children' => array(),
 664      );
 665  
 666      $operations[$id]['children']['actions'] = array(
 667        'type' => 'group',
 668        'class' => 'operations-handlers-actions-' . $id,
 669        'title' => t('Variant operations'),
 670        'children' => array(),
 671        'location' => $id,
 672      );
 673  
 674      // There needs to be a 'summary' item here for variants.
 675      $operations[$id]['children']['summary'] = array(
 676        'title' => t('Summary'),
 677        'description' => t('Get a summary of the information about this variant.'),
 678        'form info' => array(
 679          'no buttons' => TRUE,
 680        ),
 681        'form' => 'page_manager_handler_summary',
 682      );
 683  
 684      if ($plugin && isset($plugin['operations'])) {
 685        $operations[$id]['children'] += $plugin['operations'];
 686      }
 687  
 688      $actions = &$operations[$id]['children']['actions']['children'];
 689  
 690      $actions['clone'] = array(
 691        'title' => t('Clone'),
 692        'description' => t('Make an exact copy of this variant.'),
 693        'form' => 'page_manager_handler_clone',
 694      );
 695      $actions['export'] = array(
 696        'title' => t('Export'),
 697        'description' => t('Export this variant into code to import into another page.'),
 698        'form' => 'page_manager_handler_export',
 699      );
 700      if ($handler->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
 701        $actions['delete'] = array(
 702          'title' => t('Revert'),
 703          'description' => t('Remove all changes to this variant and revert to the version in code.'),
 704          'form' => 'page_manager_handler_delete',
 705          'no update and save' => TRUE,
 706          'form info' => array(
 707            'finish text' => t('Revert'),
 708          ),
 709        );
 710      }
 711      else if ($handler->export_type != EXPORT_IN_CODE) {
 712        $actions['delete'] = array(
 713          'title' => t('Delete'),
 714          'description' => t('Remove this variant from the page completely.'),
 715          'form' => 'page_manager_handler_delete',
 716          'form info' => array(
 717            'finish text' => t('Delete'),
 718            'save text' => t('Delete and save'),
 719          ),
 720        );
 721      }
 722      if (!empty($handler->disabled)) {
 723        $actions['enable'] = array(
 724          'title' => t('Enable'),
 725          'description' => t('Activate this variant so that it will be in use in your system.'),
 726          'form' => 'page_manager_handler_enable',
 727          'silent' => TRUE,
 728          'form info' => array(
 729            'finish text' => t('Enable'),
 730            'save text' => t('Enable and save'),
 731          ),
 732        );
 733      }
 734      else {
 735        $actions['disable'] = array(
 736          'title' => t('Disable'),
 737          'description' => t('De-activate this variant. The data will remain but the variant will not be in use on your system.'),
 738          'form' => 'page_manager_handler_disable',
 739          'silent' => TRUE,
 740          'form info' => array(
 741            'finish text' => t('Disable'),
 742            'save text' => t('Disable and save'),
 743          ),
 744        );
 745      }
 746  
 747      drupal_alter('page_manager_variant_operations', $operations[$id], $handler);
 748    }
 749    if (empty($operations)) {
 750      $operations['empty'] = array(
 751        'type' => 'text',
 752        'title' => t('No variants'),
 753      );
 754    }
 755  
 756    $group['children'] = $operations;
 757    return $group;
 758  }
 759  
 760  /**
 761   * Get an operation from a trail.
 762   *
 763   * @return array($operation, $active, $args)
 764   */
 765  function page_manager_get_operation($operations, $trail) {
 766    $args = $trail;
 767    $stop = FALSE;
 768    $active = array();
 769    $titles = array();
 770    // Drill down into operations array:
 771    while (!$stop) {
 772      $check = reset($args);
 773      $stop = TRUE;
 774      if (is_array($operations)) {
 775        if (isset($operations[$check])) {
 776          $active[] = $check;
 777          $operation = array_shift($args);
 778          // check to see if this operation has children. If so, we continue.
 779          if (!isset($operations[$check]['children'])) {
 780            $operations = $operations[$check];
 781          }
 782          else {
 783            $titles[] = $operations[$check]['title'];
 784            $operations = $operations[$check]['children'];
 785            // continue only if the operation hs children.
 786            $stop = FALSE;
 787          }
 788        }
 789      }
 790    }
 791  
 792    return array($operations, $active, $args, $titles);
 793  }
 794  
 795  /**
 796   * Fetch the content for an operation.
 797   *
 798   * First, this drills down through the arguments to find the operation, and
 799   * turns whatever it finds into the active trail which is then used to
 800   * hilite where we are when rendering the operation list.
 801   *
 802   * The arguments are discovered from the URL, and are an exact match for where
 803   * the operation is in the hierarchy. For example, handlers/foo/settings will
 804   * be the operation to edit the settings for the handler named foo. This comes
 805   * in as an array ('handlers', 'foo', 'settings') and is used to find where the
 806   * data for that operation is in the array.
 807   */
 808  function page_manager_get_operation_content($js, &$page, $trail, $operations) {
 809    list($operation, $active, $args, $titles) = page_manager_get_operation($operations, $trail);
 810    // Once we've found the operation, send it off to render.
 811    if ($operation) {
 812      $content = _page_manager_get_operation_content($js, $page, $active, $operation, $titles, $args);
 813    }
 814  
 815    if (empty($content)) {
 816      $content = _page_manager_get_operation_content($js, $page, array('summary'), $operations['summary']);
 817    }
 818  
 819    return $content;
 820  }
 821  
 822  /**
 823   * Fetch the content for an operation, after it's been discovered from arguments.
 824   *
 825   * This system runs through the CTools form wizard. Each operation specifies a form
 826   * or set of forms that it may use. Operations may also specify wrappers and can
 827   * set their own next/finish handlers so that they can make additional things happen
 828   * at the end.
 829   */
 830  function _page_manager_get_operation_content($js, &$page, $active, $operation, $titles = array(), $args = array()) {
 831    if (isset($operation['form'])) {
 832      $form_info = array(
 833        'id' => 'page_manager_page',
 834        'finish text' => t('Update'),
 835        'show trail' => FALSE,
 836        'show back' => FALSE,
 837        'show return' => FALSE,
 838        'show cancel' => FALSE,
 839        'next callback' => 'page_manager_edit_page_next',
 840        'finish callback' => 'page_manager_edit_page_finish',
 841        // Items specific to the 'edit' routines that will get moved over:
 842        'path' => page_manager_edit_url($page->task_name, $active) . "/%step",
 843        // wrapper function to add an extra finish button.
 844        'wrapper' => 'page_manager_operation_wrapper',
 845      );
 846  
 847      // If $operation['form'] is simply a string, then it is the function
 848      // name of the form.
 849      if (!is_array($operation['form'])) {
 850        $form_info['order'] = array(
 851          'form' => $operation['title'],
 852        );
 853        $form_info['forms'] = array(
 854          'form' => array('form id' => $operation['form']),
 855        );
 856        if (isset($operation['wrapper'])) {
 857          $form_info['forms']['form']['wrapper'] = $operation['wrapper'];
 858        }
 859      }
 860      // Otherwise it's the order and forms arrays directly.
 861      else {
 862        $form_info['order'] = $operation['form']['order'];
 863        $form_info['forms'] = $operation['form']['forms'];
 864      }
 865  
 866      // Allow the operation to override any form info settings:
 867      if (isset($operation['form info'])) {
 868        foreach ($operation['form info'] as $key => $setting) {
 869          $form_info[$key] = $setting;
 870        }
 871      }
 872  
 873      if (!empty($page->subtask['operations include'])) {
 874        // Quickly load any files necessary to display the forms.
 875        $page->subtask['operations include']['function'] = 'nop';
 876        ctools_plugin_get_function($page->subtask, 'operations include');
 877      }
 878  
 879      $step = array_shift($args);
 880      // If step is unset, go with the basic step.
 881      if (!isset($step)) {
 882        $step = current(array_keys($form_info['order']));
 883      }
 884  
 885      // If it is locked, hide the buttonzzz!
 886      if ($page->locked && empty($operation['even locked'])) {
 887        $form_info['no buttons'] = TRUE;
 888      }
 889  
 890      ctools_include('wizard');
 891      $form_state = array(
 892        'page' => $page,
 893        'type' => 'edit',
 894        'ajax' => $js && (!isset($operation['ajax']) || !empty($operation['ajax'])),
 895        'rerender' => TRUE,
 896        'trail' => $active,
 897        'task_name' => $page->task_name,
 898        'task_id' => $page->task_id,
 899        'task' => $page->task,
 900        'subtask_id' => $page->subtask_id,
 901        'subtask' => $page->subtask,
 902        'operation' => $operation,
 903      );
 904  
 905      if ($active && $active[0] == 'handlers' && isset($form_state['page']->handlers[$form_state['trail'][1]])) {
 906        $form_state['handler_id'] = $form_state['trail'][1];
 907        $form_state['handler'] = &$form_state['page']->handlers[$form_state['handler_id']];
 908      }
 909  
 910      if ($active && $active[0] == 'actions' && $active[1] == 'configure' && isset($form_state['page']->new_handler)) {
 911        $form_state['handler_id'] = $form_state['page']->new_handler->name;
 912        $form_state['handler'] = &$form_state['page']->new_handler;
 913      }
 914  
 915      $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
 916      $title = empty($form_state['title']) ? $operation['title'] : $form_state['title'];
 917      $titles[] = $title;
 918      $title = implode(' &raquo ', array_filter($titles));
 919      if (isset($form_state['js settings'])) {
 920        $js_settings = $form_state['js settings'];
 921      }
 922  
 923      // If there are messages for the form, render them.
 924      if ($messages = theme('status_messages')) {
 925        $output = $messages . $output;
 926      }
 927  
 928      $description = isset($operation['admin description']) ? $operation['admin description'] : (isset($operation['description']) ? $operation['description'] : '');
 929      $return = array(
 930        'title' => $title,
 931        'content' => $output,
 932        'description' => $description,
 933      );
 934  
 935      // Append any extra content, used for the preview which is submitted then
 936      // rendered.
 937      if (isset($form_state['content'])) {
 938        $return['content'] .= $form_state['content'];
 939      }
 940  
 941      // If the form wanted us to go somewhere else next, pass that along.
 942      if (isset($form_state['new trail'])) {
 943        $return['new trail'] = $form_state['new trail'];
 944      }
 945    }
 946    else {
 947      $return = array(
 948        'title' => t('Error'),
 949        'content' => t('This operation trail does not exist.'),
 950      );
 951    }
 952  
 953    if (isset($js_settings)) {
 954      $return['js settings'] = $js_settings;
 955    }
 956  
 957    $return['active'] = $active;
 958    return $return;
 959  }
 960  
 961  function page_manager_operation_wrapper(&$form, &$form_state) {
 962    if (empty($form_state['operation']['no update and save']) && !empty($form['buttons']['return']['#wizard type']) && $form['buttons']['return']['#wizard type']) {
 963      $form['buttons']['save'] = array(
 964        '#type' => 'submit',
 965        '#value' => !empty($form_state['form_info']['save text']) ? $form_state['form_info']['save text'] : t('Update and save'),
 966        '#wizard type' => 'finish',
 967        '#attributes' => $form['buttons']['return']['#attributes'],
 968        '#save' => TRUE,
 969      );
 970    }
 971  }
 972  
 973  /**
 974   * Callback generated when the an operation edit finished.
 975   */
 976  function page_manager_edit_page_finish(&$form_state) {
 977    if (empty($form_state['operation']['silent'])) {
 978      if (empty($form_state['clicked_button']['#save'])) {
 979        drupal_set_message(t('The page has been updated. Changes will not be permanent until you save.'));
 980      }
 981      else {
 982        drupal_set_message(t('The page has been updated and saved.'));
 983      }
 984      $path = array();
 985      foreach ($form_state['trail'] as $operation) {
 986        $path[] = $operation;
 987        $form_state['page']->changes[implode('/', $path)] = TRUE;
 988      }
 989    }
 990  
 991    // If a handler was modified, set it to changed so we know to overwrite it.
 992    if (isset($form_state['handler_id'])) {
 993      $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_CACHED;
 994    }
 995  
 996    // While we make buttons go away on locked pages, it is still possible to
 997    // have a lock a appear while you were editing, and have your changes
 998    // disappear. This at least warns the user that this has happened.
 999    if (!empty($page->locked)) {
1000      drupal_set_message(t('Unable to update changes due to lock.'));
1001    }
1002  
1003    // If the 'Update and Save' button was selected, write our cache out.
1004    if (!empty($form_state['clicked_button']['#save'])) {
1005      page_manager_save_page_cache($form_state['page']);
1006      page_manager_clear_page_cache($form_state['page']->task_name);
1007      $form_state['page'] = page_manager_get_page_cache($form_state['page']->task_name);
1008    }
1009    else {
1010      if (empty($form_state['do not cache'])) {
1011        page_manager_set_page_cache($form_state['page']);
1012      }
1013    }
1014  
1015    // We basically always want to force a rerender when the forms
1016    // are finished, so make sure there is a new trail.
1017    if (empty($form_state['new trail'])) {
1018      // force a rerender to get rid of old form items that may have changed
1019      // during save.
1020      $form_state['new trail'] = $form_state['trail'];
1021    }
1022  
1023    if (isset($form_state['new trail']) && empty($form_state['ajax'])) {
1024      $form_state['redirect'] = page_manager_edit_url($form_state['page']->task_name, $form_state['new trail']);
1025    }
1026  
1027    $form_state['complete'] = TRUE;
1028  }
1029  
1030  /**
1031   * Callback generated when the 'next' button is clicked.
1032   *
1033   * All we do here is store the cache.
1034   */
1035  function page_manager_edit_page_next(&$form_state) {
1036    page_manager_set_page_cache($form_state['page']);
1037  }
1038  
1039  /**
1040   * Callback generated when the 'cancel' button is clicked.
1041   *
1042   * All we do here is clear the cache.
1043   */
1044  function page_manager_edit_page_cancel(&$form_state) {
1045    $page = $form_state['page'];
1046  }
1047  
1048  /**
1049   * Render an operations array.
1050   *
1051   * This renders an array of operations into a series of nested UL statements,
1052   * with ajax automatically on unless specified otherwise. Operations will
1053   * automatically have the URLs generated nested.
1054   *
1055   * Each operation should have a 'type', which tells the renderer how to deal
1056   * with it:
1057   * - 'operation': An AJAX link to render. This is the default and is
1058   *   assumed if a type is not specified. Other fields for the operation:
1059   * - - 'title': The text to display. Can be an image. Must be pre-sanitized.
1060   * - - 'description': Text to place in the hover box over the link using the
1061   *     title attribute.
1062   * - - 'arguments': Anything optional to put at the end of the URL.
1063   * - - 'path': If set, overrides the default path.
1064   * - - 'no operations': If set, the path will not have operations appended.
1065   * - - 'no task': If set, the path will not have the task id.
1066   * - - 'no link': If set, this item will just be text, not a link.
1067   * - - 'ajax': If set to TRUE, ajax will be used. The default is TRUE.
1068   * - - 'class': An optional class to specify for the link.
1069   * - - 'form': The form to display for this operation, if using a single form.
1070   * - - 'forms': An array of forms that must be paired with 'order' of this
1071   *      operation uses multiple forms. See wizard tool for details.
1072   * - - 'order': The form order to use for multiple forms. See wizard tool for
1073   *     details.
1074   * - - 'form info': Form info overrides for the wizard. See the wizard tool
1075   *      for available settings
1076   * - 'group':
1077   * - - 'title': The title of the link. May be HTML.
1078   * - - 'title class': A class to apply to the title.
1079   * - - 'children': An array of more operations that this group represents.
1080   *     All operations within this group will have this group's ID as part
1081   *     of the AJAX url to make it easier to find.
1082   * - - 'class': A class to apply to the UL of the children.
1083   * - - 'collapsible': If TRUE the collapsible tool will be used.
1084   */
1085  function page_manager_render_operations(&$page, $operations, $active_trail, $attributes, $location, $parents = array()) {
1086    if (!isset($output[$location])) {
1087      $output[$location] = '';
1088    }
1089  
1090    $keys = array_keys($operations);
1091    $first = array_shift($keys);
1092    $last = array_pop($keys);
1093  
1094    // Make sure the 'first' and 'last' operations are part of THIS nav tree:
1095    while ($keys && isset($operations[$first]['location']) && $operations[$first]['location'] != $location) {
1096      $first = array_shift($keys);
1097    }
1098    while ($keys && isset($operations[$last]['location']) && $operations[$last]['location'] != $location) {
1099      $last = array_pop($keys);
1100    }
1101  
1102    $active = reset($active_trail);
1103    foreach ($operations as $id => $operation) {
1104      $current_path = '';
1105      if ($parents) {
1106        $current_path .= implode('/', $parents) . '/';
1107      }
1108      $current_path .= $id;
1109  
1110      if (empty($operation['type'])) {
1111        $operation['type'] = 'operation';
1112      }
1113  
1114      // We only render an li for things in the same nav tree.
1115      if (empty($operation['location']) || $operation['location'] == $location) {
1116        $class = $attributes['class'];
1117        if ($id == $first) {
1118          $class .= ' operation-first';
1119        }
1120        else if ($id == $last) {
1121          $class .= ' operation-last';
1122        }
1123  
1124        if (empty($operation['silent']) && !empty($page->changes[$current_path])) {
1125          $class .= $operation['type'] == 'group' ? ' changed-group' : ' changed';
1126        }
1127        else {
1128          $class .= ' not-changed';
1129        }
1130  
1131        if ($active == $id) {
1132          $class .= $operation['type'] == 'group' ? ' active-group' : ' active';
1133        }
1134        else {
1135          $class .= ' not-active';
1136        }
1137  
1138        $output[$location] .= '<li class="' . $class . '">';
1139      }
1140  
1141      switch ($operation['type']) {
1142        case 'text':
1143          $output[$location] .= $operation['title'];
1144          break;
1145        case 'operation':
1146          $path = isset($operation['path']) ? $operation['path'] : 'admin/build/pages/nojs/operation';
1147          if (!isset($operation['no task'])) {
1148            $path .= '/' . $page->task_name;
1149          }
1150  
1151          if (!isset($operation['no operations'])) {
1152            $path .= '/' . $current_path;
1153            if (isset($operation['arguments'])) {
1154              $path .= '/' . $arguments;
1155            }
1156          }
1157  
1158          $class = 'page-manager-operation';
1159          if (!isset($operation['ajax']) || !empty($operation['ajax'])) {
1160            $class .= ' ctools-use-ajax';
1161          }
1162          if (!empty($operation['class'])) {
1163            $class .= ' ' . $operation['class'];
1164          }
1165  
1166          $description = isset($operation['description']) ? $operation['description'] : '';
1167          if (empty($operation['silent']) && !empty($page->changes[$current_path])) {
1168            $description .= ' ' . t('This setting contains unsaved changes.');
1169          }
1170  
1171          $output[$location] .= l($operation['title'], $path, array('attributes' => array('id' => 'page-manager-operation-' . $id, 'class' => $class, 'title' => $description), 'html' => TRUE));
1172          break;
1173        case 'group':
1174          if ($active == $id) {
1175            $trail = $active_trail;
1176            array_shift($trail);
1177          }
1178          else {
1179            $trail = array();
1180          }
1181          $group_location = isset($operation['location']) ? $operation['location'] : $location;
1182          $temp = page_manager_render_operations($page, $operation['children'], $trail, $operation, $group_location, array_merge($parents, array($id)));
1183          if ($temp) {
1184            foreach ($temp as $id => $text) {
1185              if (empty($output[$id])) {
1186                $output[$id] = '';
1187              }
1188              $output[$id] .= $text;
1189            }
1190          }
1191          break;
1192      }
1193  
1194      if (empty($operation['location']) || $operation['location'] == $location) {
1195        $output[$location] .= '</li>';
1196      }
1197    }
1198  
1199    if ($output[$location]) {
1200      $output[$location] = '<ul class="page-manager-operations ' . $attributes['class'] . '">' . $output[$location] . '</ul>';
1201  
1202      if (!empty($attributes['title'])) {
1203        $class = '';
1204        if (isset($attributes['title class'])) {
1205          $class = $attributes['title class'];
1206        }
1207        $title = '<div class="page-manager-group-title' . $class . '">' . $attributes['title'] . '</div>';
1208  
1209        if (!empty($attributes['collapsible'])) {
1210          $output[$location] = theme('ctools_collapsible', $title, $output[$location], empty($active_trail));
1211        }
1212        else {
1213          $output[$location] = $title . $output[$location];
1214        }
1215      }
1216      return $output;
1217    }
1218  }
1219  
1220  /**
1221   * Provide a simple form for saving the page manager info out of the cache.
1222   */
1223  function page_manager_save_page_form(&$form_state) {
1224    if (!empty($form_state['page']->changed)) {
1225      $form['markup'] = array(
1226        '#value' => '<div class="changed-notification">' . t('You have unsaved changes to this page. You must select Save to write them to the database, or Cancel to discard these changes. Please note that if you have changed any form, you must submit that form before saving.') . '</div>',
1227      );
1228  
1229      // Always make sure we submit back to the proper page.
1230      $form['#action'] = url('admin/build/pages/edit/' . $form_state['page']->task_name);
1231      $form['save'] = array(
1232        '#type' => 'submit',
1233        '#value' => t('Save'),
1234        '#submit' => array('page_manager_save_page_form_submit'),
1235      );
1236  
1237      $form['cancel'] = array(
1238        '#type' => 'submit',
1239        '#value' => t('Cancel'),
1240        '#submit' => array('page_manager_save_page_form_cancel'),
1241      );
1242      return $form;
1243    }
1244  }
1245  
1246  /**
1247   * Save the page.
1248   */
1249  function page_manager_save_page_form_submit(&$form, &$form_state) {
1250    page_manager_save_page_cache($form_state['page']);
1251  }
1252  
1253  /**
1254   * Discard changes to the page.
1255   */
1256  function page_manager_save_page_form_cancel(&$form, &$form_state) {
1257    drupal_set_message(t('All pending changes have been discarded, and the page is now unlocked.'));
1258    page_manager_clear_page_cache($form_state['page']->task_name);
1259  
1260    if (!empty($form_state['page']->new)) {
1261      $form_state['redirect'] = 'admin/build/pages';
1262    }
1263  }
1264  
1265  // --------------------------------------------------------------------------
1266  // Handler (variant) related forms.
1267  
1268  /**
1269   * Add a new task handler.
1270   */
1271  function page_manager_handler_add(&$form, &$form_state) {
1272    // Get a list of possible task handlers for this task.
1273    page_manager_handler_add_form($form, $form_state);
1274  }
1275  
1276  /**
1277   * Handler related forms.
1278   */
1279  function page_manager_handler_add_submit(&$form, &$form_state) {
1280    $cache = $form_state['page'];
1281    $plugin = page_manager_get_task_handler($form_state['values']['handler']);
1282  
1283    // Create a new handler.
1284    $handler = page_manager_new_task_handler($plugin);
1285    if (!empty($form_state['values']['title'])) {
1286      $handler->conf['title'] = $form_state['values']['title'];
1287    }
1288    else {
1289      $handler->conf['title'] = $plugin['title'];
1290    }
1291    $cache->new_handler = $handler;
1292  
1293    // Figure out which forms to present them with
1294    $cache->forms = array();
1295  
1296    $features = $form_state['values']['features'];
1297    if (isset($features[$form_state['values']['handler']])) {
1298      $cache->forms = array_merge($cache->forms, array_keys(array_filter($features[$form_state['values']['handler']])));
1299    }
1300  
1301    if (isset($plugin['required forms'])) {
1302      $cache->forms = array_merge($cache->forms, array_keys($plugin['required forms']));
1303    }
1304  
1305    $form_state['no_rerender'] = TRUE;
1306    if (!empty($cache->forms)) {
1307      // Tell the form to go to the config page.
1308      drupal_set_message(t('Before this variant can be added, it must be configured. When you are finished, click "Create variant" at the end of this wizard to add this to your page.'));
1309      $form_state['new trail'] = array('actions', 'configure');
1310    }
1311    else {
1312      // It has no forms at all. Add the variant and go to its first operation.
1313      page_manager_handler_add_finish($form_state);
1314    }
1315  }
1316  
1317  /**
1318   * Finish the add process and make the new handler official.
1319   */
1320  function page_manager_handler_add_finish(&$form_state) {
1321    $page = &$form_state['page'];
1322    $handler = $page->new_handler;
1323    page_manager_handler_add_to_page($page, $handler);
1324  
1325    // Remove the temporary page.
1326    unset($page->new_handler);
1327    unset($page->forms);
1328  
1329    // Set the new destination
1330    $plugin = page_manager_get_task_handler($handler->handler);
1331    if (!empty($plugin['add finish'])) {
1332      $location = $plugin['add finish'];
1333    }
1334    else {
1335      $keys = array_keys($plugin['operations']);
1336      $location = reset($keys);
1337    }
1338  
1339    $form_state['new trail'] = array('handlers', $handler->name, $location);
1340  
1341    // Pass through.
1342    page_manager_edit_page_finish($form_state);
1343  }
1344  
1345  /**
1346   * Throw away a new handler and return to the add form
1347   */
1348  function page_manager_handler_add_cancel(&$form_state) {
1349    $form_state['new trail'] = array('handlers', 'add');
1350  
1351    // Remove the temporary page.
1352    unset($page->new_handler);
1353    unset($page->forms);
1354  }
1355  
1356  /**
1357   * Provide a consistent UI for adding handlers.
1358   */
1359  function page_manager_handler_add_form(&$form, $form_state, $features = array()) {
1360    $task = $form_state['task'];
1361    $task_handler_plugins = page_manager_get_task_handler_plugins($task);
1362    if (empty($task_handler_plugins)) {
1363      drupal_set_message(t('There are currently no variants available and a page may not be added. Perhaps you need to install the Panels module to get a variant?'), 'error');
1364      $form['buttons']['return']['#disabled'] = TRUE;
1365      return;
1366    }
1367  
1368    foreach ($task_handler_plugins as $id => $plugin) {
1369      $options[$id] = $plugin['title'];
1370      if (isset($plugin['add features'])) {
1371        $features[$id] = $plugin['add features'];
1372      }
1373    }
1374  
1375    if (!isset($form_state['type']) || $form_state['type'] != 'add') {
1376      $form['title'] = array(
1377        '#type' => 'textfield',
1378        '#title' => t('Title'),
1379        '#description' => t('Administrative title of this variant. If you leave blank it will be automatically assigned.'),
1380      );
1381    }
1382  
1383    $form['handler'] = array(
1384      '#title' => t('Variant type'),
1385      '#type' => 'select',
1386      '#options' => $options,
1387    );
1388  
1389    // This set of checkboxes is not dangerous at all.
1390    $form['features'] = array(
1391      '#type' => 'checkboxes',
1392      '#validated' => TRUE,
1393      '#title' => t('Optional features'),
1394      '#options' => array(),
1395      '#description' => t('Check any optional features you need to be presented with forms for configuring them. If you do not check them here you will still be able to utilize these features once the new page is created. If you are not sure, leave these unchecked.'),
1396      '#tree' => TRUE,
1397    );
1398  
1399    ctools_include('dependent');
1400    foreach ($features as $plugin => $feature_list) {
1401      foreach ($feature_list as $feature_id => $feature) {
1402        $form['features'][$plugin][$feature_id] = array(
1403          '#type' => 'checkbox',
1404          '#title' => $feature,
1405        );
1406        if (!empty($form_state['page']->forms) && in_array($feature_id, $form_state['page']->forms)) {
1407          $form['features'][$plugin][$feature_id]['#default_value'] = TRUE;
1408        }
1409  
1410        if ($plugin != 'default') {
1411          $form['features'][$plugin][$feature_id] += array(
1412            '#process' => array('ctools_dependent_process'),
1413            '#dependency' => array('edit-handler' => array($plugin)),
1414          );
1415        }
1416      }
1417    }
1418  }
1419  
1420  /**
1421   * Rearrange the order of variants.
1422   */
1423  function page_manager_handler_import(&$form, &$form_state) {
1424    $form['title'] = array(
1425      '#type' => 'textfield',
1426      '#title' => t('Variant name'),
1427      '#description' => t('Enter the name of the new variant.'),
1428    );
1429  
1430    if (user_access('use PHP for block visibility')) {
1431      $form['object'] = array(
1432        '#type' => 'textarea',
1433        '#title' => t('Paste variant code here'),
1434        '#rows' => 15,
1435      );
1436    }
1437    // Users ordinarily can't get here without the PHP block visibility perm.
1438    // In case they somehow do, though, disable the form widget for extra safety.
1439    else {
1440      $form['shoveoff'] = array(
1441        '#value' => '<div>' . t('You do not have sufficient permissions to perform this action.') . '</div>',
1442      );
1443    }
1444  }
1445  
1446  /**
1447   * Make sure that an import actually provides a handler.
1448   */
1449  function page_manager_handler_import_validate($form, &$form_state) {
1450    if (!user_access('use PHP for block visibility')) {
1451      form_error($form['shoveoff'], t('You account permissions do not permit you to import.'));
1452      return;
1453    }
1454    ob_start();
1455    eval($form_state['values']['object']);
1456    ob_end_clean();
1457  
1458    if (empty($handler)) {
1459      $errors = ob_get_contents();
1460      if (empty($errors)) {
1461        $errors = t('No variant found.');
1462      }
1463  
1464      form_error($form['object'], t('Unable to get a variant from the import. Errors reported: @errors', array('@errors' => $errors)));
1465    }
1466  
1467    $form_state['handler'] = $handler;
1468  }
1469  
1470  /**
1471   * Clone an existing task handler into a new handler.
1472   */
1473  function page_manager_handler_import_submit($form, &$form_state) {
1474    $handler = $form_state['handler'];
1475  
1476    page_manager_handler_add_to_page($form_state['page'], $handler, $form_state['values']['title']);
1477  
1478    $plugin = page_manager_get_task_handler($handler->handler);
1479    // It has no forms at all. Add the variant and go to its first operation.
1480    $keys = array_keys($plugin['operations']);
1481    $form_state['new trail'] = array('handlers', $handler->name, reset($keys));
1482  }
1483  
1484  /**
1485   * Rearrange the order of variants.
1486   */
1487  function page_manager_handler_rearrange(&$form, &$form_state) {
1488    $page = $form_state['page'];
1489  
1490    $form['handlers'] = array('#tree' => TRUE);
1491  
1492    foreach ($page->handler_info as $id => $info) {
1493      if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
1494        continue;
1495      }
1496      $handler = $page->handlers[$id];
1497      $plugin = page_manager_get_task_handler($handler->handler);
1498  
1499      $form['handlers'][$id]['title'] = array(
1500        '#value' => page_manager_get_handler_title($plugin, $handler, $page->task, $page->subtask_id),
1501      );
1502  
1503      $form['handlers'][$id]['weight'] = array(
1504        '#type' => 'weight',
1505        '#default_value' => $info['weight'],
1506        '#delta' => 30,
1507      );
1508    }
1509  }
1510  
1511  function page_manager_handler_rearrange_submit(&$form, &$form_state) {
1512    $handler_info = &$form_state['page']->handler_info;
1513  
1514    foreach ($form_state['values']['handlers'] as $id => $info) {
1515      if ($handler_info[$id]['weight'] = $info['weight']) {
1516        $handler_info[$id]['weight'] = $info['weight'];
1517        $handler_info[$id]['changed'] |= PAGE_MANAGER_CHANGED_MOVED;
1518      }
1519    }
1520  
1521    // Sort the new cache.
1522    uasort($handler_info, '_page_manager_handler_sort');
1523  
1524  }
1525  
1526  /**
1527   * Used as a callback to uasort to sort the task cache by weight.
1528   *
1529   * The 'name' field is used as a backup when weights are the same, which
1530   * can happen when multiple modules put items out there at the same
1531   * weight.
1532   */
1533  function _page_manager_handler_sort($a, $b) {
1534    if ($a['weight'] < $b['weight']) {
1535      return -1;
1536    }
1537    elseif ($a['weight'] > $b['weight']) {
1538      return 1;
1539    }
1540    elseif ($a['name'] < $b['name']) {
1541      return -1;
1542    }
1543    elseif ($a['name'] > $b['name']) {
1544      return 1;
1545    }
1546  }
1547  
1548  /**
1549   * Rearrange the order of variants.
1550   */
1551  function page_manager_handler_delete(&$form, &$form_state) {
1552    if ($form_state['handler']->type == t('Overridden')) {
1553      $text = t('Reverting the variant will delete the variant that is in the database, reverting it to the original default variant. This deletion will not be made permanent until you click Save.');
1554    }
1555    else {
1556      $text = t('Are you sure you want to delete this variant? This deletion will not be made permanent until you click Save.');
1557    }
1558    $form['markup'] = array(
1559      '#value' => '<p>' . $text . '</p>',
1560    );
1561  
1562  }
1563  
1564  /**
1565   * Submit handler to delete a view.
1566   */
1567  function page_manager_handler_delete_submit(&$form, &$form_state) {
1568    $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_DELETED;
1569    $form_state['new trail'] = array('summary');
1570  }
1571  
1572  /**
1573   * Entry point to export a page.
1574   */
1575  function page_manager_handler_export(&$form, &$form_state) {
1576    $export = page_manager_export_task_handler($form_state['handler']);
1577  
1578    $lines = substr_count($export, "\n");
1579    $form['code'] = array(
1580      '#type' => 'textarea',
1581      '#default_value' => $export,
1582      '#rows' => $lines,
1583    );
1584  
1585    unset($form['buttons']);
1586  }
1587  
1588  /**
1589   * Rearrange the order of variants.
1590   */
1591  function page_manager_handler_clone(&$form, &$form_state) {
1592    // This provides its own button because it does something totally different.
1593    $form['title'] = array(
1594      '#type' => 'textfield',
1595      '#title' => t('Variant name'),
1596      '#description' => t('Enter the name of the new variant.'),
1597    );
1598  }
1599  
1600  /**
1601   * Clone an existing task handler into a new handler.
1602   */
1603  function page_manager_handler_clone_submit($form, &$form_state) {
1604    $export = page_manager_export_task_handler($form_state['handler']);
1605    ob_start();
1606    eval($export);
1607    ob_end_clean();
1608  
1609    page_manager_handler_add_to_page($form_state['page'], $handler, $form_state['values']['title']);
1610  
1611    $plugin = page_manager_get_task_handler($handler->handler);
1612    // It has no forms at all. Add the variant and go to its first operation.
1613    $keys = array_keys($plugin['operations']);
1614    $form_state['new trail'] = array('handlers', $handler->name, reset($keys));
1615  }
1616  
1617  /**
1618   * Form to enable a handler.
1619   */
1620  function page_manager_handler_enable(&$form, &$form_state) {
1621    $form['markup'] = array(
1622      '#value' => t('This variant is currently disabled. Enabling it will make it available in your system. This will not take effect until you save this page.'),
1623    );
1624  }
1625  
1626  /**
1627   * Enable the page after it has been confirmed.
1628   */
1629  function page_manager_handler_enable_submit(&$form, &$form_state) {
1630    $form_state['handler']->disabled = FALSE;
1631    $form_state['page']->handler_info[$form_state['handler_id']]['disabled'] = FALSE;
1632    $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_STATUS;
1633    $form_state['new trail'] = array('handlers', $form_state['handler_id'], 'actions', 'disable');
1634  }
1635  
1636  /**
1637   * Form to disable a page.
1638   */
1639  function page_manager_handler_disable(&$form, &$form_state) {
1640    $form['markup'] = array(
1641      '#value' => t('This variant is currently enabled. Disabling it will make it unavailable in your system, and it will not be used. This will not take effect until you save this page.'),
1642    );
1643  }
1644  
1645  /**
1646   * Form to disable a page.
1647   */
1648  function page_manager_handler_summary(&$form, &$form_state) {
1649    $handler = $form_state['handler'];
1650    $page = $form_state['page'];
1651    $plugin = page_manager_get_task_handler($handler->handler);
1652  
1653    $form['markup'] = array(
1654      '#value' => page_manager_get_handler_summary($plugin, $handler, $page, FALSE),
1655    );
1656  }
1657  
1658  /**
1659   * Disable the page after it has been confirmed.
1660   */
1661  function page_manager_handler_disable_submit(&$form, &$form_state) {
1662    $form_state['handler']->disabled = TRUE;
1663    $form_state['page']->handler_info[$form_state['handler_id']]['disabled'] = TRUE;
1664    $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_STATUS;
1665    $form_state['new trail'] = array('handlers', $form_state['handler_id'], 'actions', 'enable');
1666  }
1667  
1668  /**
1669   * Break the lock on a page so that it can be edited.
1670   */
1671  function page_manager_break_lock(&$form, &$form_state) {
1672    $form['markup'] = array(
1673      '#value' => t('Breaking the lock on this page will <strong>discard</strong> any pending changes made by the locking user. Are you REALLY sure you want to do this?')
1674    );
1675  }
1676  
1677  /**
1678   * Submit to break the lock on a page.
1679   */
1680  function page_manager_break_lock_submit(&$form, &$form_state) {
1681    $page = &$form_state['page'];
1682    $form_state['page']->locked = FALSE;
1683    ctools_object_cache_clear_all('page_manager_page', $page->task_name);
1684    $form_state['do not cache'] = TRUE;
1685    drupal_set_message(t('The lock has been cleared and all changes discarded. You may now make changes to this page.'));
1686  
1687    $form_state['new trail'] = array('summary');
1688  }
1689  
1690  /**
1691   * Form to enable a page.
1692   */
1693  function page_manager_enable_form(&$form, &$form_state) {
1694    $form['markup'] = array(
1695      '#value' => t('Enabling this page will immediately make it available in your system (there is no need to wait for a save.)'),
1696    );
1697  }
1698  
1699  /**
1700   * Enable the page after it has been confirmed.
1701   */
1702  function page_manager_enable_form_submit(&$form, &$form_state) {
1703    $page = &$form_state['page'];
1704    if ($function = ctools_plugin_get_function($page->subtask, 'enable callback')) {
1705      $result = $function($page, FALSE);
1706      menu_rebuild();
1707    }
1708    $form_state['new trail'] = array('actions', 'disable');
1709  
1710    // We don't want to cause this to cache if it wasn't already. If it was
1711    // cached, however, we want to update the enabled state.
1712    if (empty($form_state['page']->changed)) {
1713      $form_state['do not cache'] = TRUE;
1714    }
1715  }
1716  
1717  /**
1718   * Form to disable a page.
1719   */
1720  function page_manager_disable_form(&$form, &$form_state) {
1721    $form['markup'] = array(
1722      '#value' => t('Disabling this page will immediately make it unavailable in your system (there is no need to wait for a save.)'),
1723    );
1724  }
1725  
1726  /**
1727   * Disable the page after it has been confirmed.
1728   */
1729  function page_manager_disable_form_submit(&$form, &$form_state) {
1730    $page = &$form_state['page'];
1731    if ($function = ctools_plugin_get_function($page->subtask, 'enable callback')) {
1732      $result = $function($page, TRUE);
1733        menu_rebuild();
1734      $form_state['new trail'] = array('actions', 'enable');
1735  
1736      // We don't want to cause this to cache if it wasn't already. If it was
1737      // cached, however, we want to update the enabled state.
1738      if (empty($form_state['page']->changed)) {
1739        $form_state['do not cache'] = TRUE;
1740      }
1741    }
1742  }
1743  
1744  /**
1745   * Print the summary information for a page.
1746   */
1747  function page_manager_page_summary(&$form, &$form_state) {
1748    $page = $form_state['page'];
1749  
1750    $output = '';
1751  
1752  /*
1753    if (isset($form_state['subtask']['admin title'])) {
1754      $form_state['title'] = $form_state['subtask']['admin title'];
1755    }
1756  */
1757  
1758    if (isset($form_state['subtask']['admin description'])) {
1759      $output .= '<div class="description">' . $form_state['subtask']['admin description'] . '</div>';
1760    }
1761  
1762    $output .= page_manager_get_page_summary($page->task, $page->subtask);
1763  
1764    if (!empty($page->handlers)) {
1765      foreach ($page->handler_info as $id => $info) {
1766        if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
1767          continue;
1768        }
1769  
1770        $handler = $page->handlers[$id];
1771        $plugin = page_manager_get_task_handler($handler->handler);
1772  
1773        $output .= '<div class="handler-summary">';
1774        $output .= page_manager_get_handler_summary($plugin, $handler, $page);
1775        $output .= '</div>';
1776  
1777      }
1778    }
1779    else {
1780      $output .= '<p>' . t('This page has no variants and thus no output of its own.') . '</p>';
1781    }
1782  
1783    $links = array(
1784      array(
1785        'title' => ' &raquo; ' . t('Add a new variant'),
1786        'href' => page_manager_edit_url($page->task_name, array('actions', 'add')),
1787        'html' => TRUE,
1788      ),
1789    );
1790  
1791    $output .= '<div class="links">' . theme('links', $links) . '</div>';
1792    $form['markup'] = array(
1793      '#value' => $output,
1794    );
1795  }
1796  
1797  /**
1798   * Menu callback to enable or disable a page
1799   */
1800  function page_manager_enable_page($disable, $js, $page) {
1801    if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], $page->task_name)) {
1802      return MENU_ACCESS_DENIED;
1803    }
1804    if ($page->locked) {
1805      if ($disable) {
1806        drupal_set_message(t('Unable to disable due to lock.'));
1807      }
1808      else {
1809        drupal_set_message(t('Unable to enable due to lock.'));
1810      }
1811    }
1812    else {
1813      if ($function = ctools_plugin_get_function($page->subtask, 'enable callback')) {
1814        $result = $function($page, $disable);
1815        menu_rebuild();
1816  
1817        // We want to re-cache this if it's changed so that status is properly
1818        // updated on the changed form.
1819        if (!empty($page->changed)) {
1820          page_manager_set_page_cache($page);
1821        }
1822      }
1823    }
1824  
1825    // For now $js is not actually in use on this.
1826    drupal_goto('admin/build/pages');
1827  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7