context-admin.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. <?php
  2. /**
  3. * @file includes/common-context.inc
  4. * Provide API for adding contexts for modules that embed displays.
  5. *
  6. * Note that most of this code was directly copied from Panels 2, and as such
  7. * a lot of this code is crusty. It could probably stand to be rewritten,
  8. * and brought up to date, or at least better commented.
  9. */
  10. /**
  11. * Provide a list of the ways contexts can be embedded.
  12. *
  13. * This provides a full list of context types that the tool understands
  14. * and can let modules utilize.
  15. */
  16. function ctools_context_info($type = NULL) {
  17. static $info = NULL;
  18. // static doesn't work with functions like t().
  19. if (empty($info)) {
  20. $info = array(
  21. 'argument' => array(
  22. 'title' => t('Arguments'),
  23. 'singular title' => t('argument'),
  24. 'description' => '', // t("Arguments are parsed from the URL and translated into contexts that may be added to the display via the 'content' tab. These arguments are parsed in the order received, and you may use % in your URL to hold the place of an object; the rest of the arguments will come after the URL. For example, if the URL is node/%/panel and your user visits node/1/panel/foo, the first argument will be 1, and the second argument will be foo."),
  25. 'add button' => t('Add argument'),
  26. 'context function' => 'ctools_get_argument',
  27. 'key' => 'arguments', // the key that data will be stored on an object, eg $panel_page
  28. 'sortable' => TRUE,
  29. 'settings' => 'argument_settings',
  30. ),
  31. 'relationship' => array(
  32. 'title' => t('Relationships'),
  33. 'singular title' => t('relationship'),
  34. 'description' => '', // t('Relationships are contexts that are created from already existing contexts; the add relationship button will only appear once there is another context available. Relationships can load objects based upon how they are related to each other; for example, the author of a node, or a taxonomy term attached to a node, or the vocabulary of a taxonomy term.'),
  35. 'add button' => t('Add relationship'),
  36. 'context function' => 'ctools_get_relationship',
  37. 'key' => 'relationships',
  38. 'sortable' => FALSE,
  39. 'settings' => 'relationship_settings',
  40. ),
  41. 'context' => array(
  42. 'title' => t('Contexts'),
  43. 'singular title' => t('context'),
  44. 'description' => '', // t('Contexts are embedded directly into the panel; you generally must select an object in the panel. For example, you could select node 5, or the term "animals" or the user "administrator"'),
  45. 'add button' => t('Add context'),
  46. 'context function' => 'ctools_get_context',
  47. 'key' => 'contexts',
  48. 'sortable' => FALSE,
  49. 'settings' => 'context_settings',
  50. ),
  51. 'requiredcontext' => array(
  52. 'title' => t('Required contexts'),
  53. 'singular title' => t('required context'),
  54. 'description' => '', // t('Required contexts are passed in from some external source, such as a containing panel. If a mini panel has required contexts, it can only appear when that context is available, and therefore will not show up as a standard Drupal block.'),
  55. 'add button' => t('Add required context'),
  56. 'context function' => 'ctools_get_context',
  57. 'key' => 'requiredcontexts',
  58. 'sortable' => FALSE,
  59. ),
  60. );
  61. }
  62. if ($type === NULL) {
  63. return $info;
  64. }
  65. return $info[$type];
  66. }
  67. /**
  68. * Get the data belonging to a particular context.
  69. */
  70. function ctools_context_get_plugin($type, $name) {
  71. $info = ctools_context_info($type);
  72. if (function_exists($info['context function'])) {
  73. return $info['context function']($name);
  74. }
  75. }
  76. /**
  77. * Add the argument table plus gadget plus javascript to the form.
  78. */
  79. function ctools_context_add_argument_form($module, &$form, &$form_state, &$form_location, $object, $cache_key = NULL) {
  80. if (empty($cache_key)) {
  81. $cache_key = $object->name;
  82. }
  83. $form_location = array(
  84. '#prefix' => '<div id="ctools-arguments-table">',
  85. '#suffix' => '</div>',
  86. '#theme' => 'ctools_context_item_form',
  87. '#cache_key' => $cache_key,
  88. '#ctools_context_type' => 'argument',
  89. '#ctools_context_module' => $module,
  90. );
  91. $args = ctools_get_arguments();
  92. $choices = array();
  93. foreach ($args as $name => $arg) {
  94. if (empty($arg['no ui'])) {
  95. $choices[$name] = $arg['title'];
  96. }
  97. }
  98. asort($choices);
  99. if (!empty($choices) || !empty($object->arguments)) {
  100. ctools_context_add_item_table('argument', $form_location, $choices, $object->arguments);
  101. }
  102. }
  103. function ctools_context_add_context_form($module, &$form, &$form_state, &$form_location, $object, $cache_key = NULL) {
  104. if (empty($cache_key)) {
  105. $cache_key = $object->name;
  106. }
  107. $form_location = array(
  108. '#prefix' => '<div id="ctools-contexts-table">',
  109. '#suffix' => '</div>',
  110. '#theme' => 'ctools_context_item_form',
  111. '#cache_key' => $cache_key,
  112. '#ctools_context_type' => 'context',
  113. '#ctools_context_module' => $module,
  114. );
  115. // Store the order the choices are in so javascript can manipulate it.
  116. $form_location['markup'] = array(
  117. '#markup' => '&nbsp;',
  118. );
  119. $choices = array();
  120. foreach (ctools_get_contexts() as $name => $arg) {
  121. if (empty($arg['no ui'])) {
  122. $choices[$name] = $arg['title'];
  123. }
  124. }
  125. asort($choices);
  126. if (!empty($choices) || !empty($object->contexts)) {
  127. ctools_context_add_item_table('context', $form_location, $choices, $object->contexts);
  128. }
  129. }
  130. function ctools_context_add_required_context_form($module, &$form, &$form_state, &$form_location, $object, $cache_key = NULL) {
  131. if (empty($cache_key)) {
  132. $cache_key = $object->name;
  133. }
  134. $form_location = array(
  135. '#prefix' => '<div id="ctools-requiredcontexts-table">',
  136. '#suffix' => '</div>',
  137. '#theme' => 'ctools_context_item_form',
  138. '#cache_key' => $cache_key,
  139. '#ctools_context_type' => 'requiredcontext',
  140. '#ctools_context_module' => $module,
  141. );
  142. // Store the order the choices are in so javascript can manipulate it.
  143. $form_location['markup'] = array(
  144. '#value' => '&nbsp;',
  145. );
  146. $choices = array();
  147. foreach (ctools_get_contexts() as $name => $arg) {
  148. if (empty($arg['no required context ui'])) {
  149. $choices[$name] = $arg['title'];
  150. }
  151. }
  152. asort($choices);
  153. if (!empty($choices) || !empty($object->contexts)) {
  154. ctools_context_add_item_table('requiredcontext', $form_location, $choices, $object->requiredcontexts);
  155. }
  156. }
  157. function ctools_context_add_relationship_form($module, &$form, &$form_state, &$form_location, $object, $cache_key = NULL) {
  158. if (empty($cache_key)) {
  159. $cache_key = $object->name;
  160. }
  161. $form_location = array(
  162. '#prefix' => '<div id="ctools-relationships-table">',
  163. '#suffix' => '</div>',
  164. '#theme' => 'ctools_context_item_form',
  165. '#cache_key' => $cache_key,
  166. '#ctools_context_type' => 'relationship',
  167. '#ctools_context_module' => $module,
  168. );
  169. // Store the order the choices are in so javascript can manipulate it.
  170. $form_location['markup'] = array(
  171. '#value' => '&nbsp;',
  172. );
  173. $base_contexts = isset($object->base_contexts) ? $object->base_contexts : array();
  174. $available_relationships = ctools_context_get_relevant_relationships(ctools_context_load_contexts($object, TRUE, $base_contexts));
  175. ctools_context_add_item_table('relationship', $form_location, $available_relationships, $object->relationships);
  176. }
  177. /**
  178. * Include all context administrative include files, css, javascript.
  179. */
  180. function ctools_context_admin_includes() {
  181. ctools_include('context');
  182. ctools_include('modal');
  183. ctools_include('ajax');
  184. ctools_include('object-cache');
  185. ctools_modal_add_js();
  186. ctools_modal_add_plugin_js(ctools_get_contexts());
  187. ctools_modal_add_plugin_js(ctools_get_relationships());
  188. }
  189. /**
  190. * Add the context table to the page.
  191. */
  192. function ctools_context_add_item_table($type, &$form, $available_contexts, $items) {
  193. $form[$type] = array(
  194. '#tree' => TRUE,
  195. );
  196. $module = $form['#ctools_context_module'];
  197. $cache_key = $form['#cache_key'];
  198. if (isset($items) && is_array($items)) {
  199. foreach ($items as $position => $context) {
  200. ctools_context_add_item_to_form($module, $type, $cache_key, $form[$type][$position], $position, $context);
  201. }
  202. }
  203. $type_info = ctools_context_info($type);
  204. $form['description'] = array(
  205. '#prefix' => '<div class="description">',
  206. '#suffix' => '</div>',
  207. '#markup' => $type_info['description'],
  208. );
  209. ctools_context_add_item_table_buttons($type, $module, $form, $available_contexts);
  210. }
  211. function ctools_context_add_item_table_buttons($type, $module, &$form, $available_contexts) {
  212. drupal_add_library('system', 'drupal.ajax');
  213. $form['buttons'] = array(
  214. '#tree' => TRUE,
  215. );
  216. if (!empty($available_contexts)) {
  217. $type_info = ctools_context_info($type);
  218. $module = $form['#ctools_context_module'];
  219. $cache_key = $form['#cache_key'];
  220. // The URL for this ajax button
  221. $form['buttons'][$type]['add-url'] = array(
  222. '#attributes' => array('class' => array("ctools-$type-add-url")),
  223. '#type' => 'hidden',
  224. '#value' => url("ctools/context/ajax/add/$module/$type/$cache_key", array('absolute' => TRUE)),
  225. );
  226. asort($available_contexts);
  227. // This also will be in the URL.
  228. $form['buttons'][$type]['item'] = array(
  229. '#attributes' => array('class' => array("ctools-$type-add-url")),
  230. '#type' => 'select',
  231. '#options' => $available_contexts,
  232. '#required' => FALSE,
  233. );
  234. $form['buttons'][$type]['add'] = array(
  235. '#type' => 'submit',
  236. '#attributes' => array('class' => array('ctools-use-modal')),
  237. '#id' => "ctools-$type-add",
  238. '#value' => $type_info['add button'],
  239. );
  240. }
  241. }
  242. /**
  243. * Add a row to the form. Used both in the main form and by
  244. * the ajax to add an item.
  245. */
  246. function ctools_context_add_item_to_form($module, $type, $cache_key, &$form, $position, $item) {
  247. // This is the single function way to load any plugin by variable type.
  248. $info = ctools_context_get_plugin($type, $item['name']);
  249. $form['title'] = array(
  250. '#markup' => check_plain($item['identifier']),
  251. );
  252. // Relationships not sortable.
  253. $type_info = ctools_context_info($type);
  254. if (!empty($type_info['sortable'])) {
  255. $form['position'] = array(
  256. '#type' => 'weight',
  257. '#default_value' => $position,
  258. '#attributes' => array('class' => array('drag-position')),
  259. );
  260. }
  261. $form['remove'] = array(
  262. '#markup' => ctools_ajax_image_button(ctools_image_path('icon-delete.png'), "ctools/context/ajax/delete/$module/$type/$cache_key/$position", t('Remove this item.')),
  263. );
  264. $form['settings'] = array(
  265. '#markup' => ctools_modal_image_button(ctools_image_path('icon-configure.png'), "ctools/context/ajax/configure/$module/$type/$cache_key/$position", t('Configure settings for this item.')),
  266. );
  267. }
  268. // ---------------------------------------------------------------------------
  269. // AJAX forms and stuff.
  270. /**
  271. * Ajax entry point to add an context
  272. */
  273. function ctools_context_ajax_item_add($mechanism = NULL, $type = NULL, $cache_key = NULL, $name = NULL, $step = NULL) {
  274. ctools_include('ajax');
  275. ctools_include('modal');
  276. ctools_include('context');
  277. ctools_include('cache');
  278. ctools_include('plugins-admin');
  279. if (!$name) {
  280. return ctools_ajax_render_error();
  281. }
  282. // Load stored object from cache.
  283. if (!($object = ctools_cache_get($mechanism, $cache_key))) {
  284. ctools_ajax_render_error(t('Invalid object name.'));
  285. }
  286. // Get info about what we're adding, i.e, relationship, context, argument, etc.
  287. $plugin_definition = ctools_context_get_plugin($type, $name);
  288. if (empty($plugin_definition)) {
  289. ctools_ajax_render_error(t('Invalid context type'));
  290. }
  291. // Set up the $conf array for this plugin
  292. if (empty($step) || empty($object->temporary)) {
  293. // Create the basis for our new context.
  294. $conf = ctools_context_get_defaults($plugin_definition, $object, $type);
  295. $object->temporary = &$conf;
  296. }
  297. else {
  298. $conf = &$object->temporary;
  299. }
  300. // Load the contexts that may be used.
  301. $base_contexts = isset($object->base_contexts) ? $object->base_contexts : array();
  302. $contexts = ctools_context_load_contexts($object, TRUE, $base_contexts);
  303. $type_info = ctools_context_info($type);
  304. $form_state = array(
  305. 'ajax' => TRUE,
  306. 'modal' => TRUE,
  307. 'modal return' => TRUE,
  308. 'object' => &$object,
  309. 'conf' => &$conf,
  310. 'plugin' => $plugin_definition,
  311. 'type' => $type,
  312. 'contexts' => $contexts,
  313. 'title' => t('Add @type "@context"', array('@type' => $type_info['singular title'], '@context' => $plugin_definition['title'])),
  314. 'type info' => $type_info,
  315. 'op' => 'add',
  316. 'step' => $step,
  317. );
  318. $form_info = array(
  319. 'path' => "ctools/context/ajax/add/$mechanism/$type/$cache_key/$name/%step",
  320. 'show cancel' => TRUE,
  321. 'default form' => 'ctools_edit_context_form_defaults',
  322. 'auto caching' => TRUE,
  323. 'cache mechanism' => $mechanism,
  324. 'cache key' => $cache_key,
  325. // This is stating what the cache will be referred to in $form_state
  326. 'cache storage' => 'object',
  327. );
  328. if ($type == 'requiredcontext') {
  329. $form_info += array(
  330. 'add form name' => 'required context add form',
  331. 'edit form name' => 'required context edit form',
  332. );
  333. }
  334. $output = ctools_plugin_configure_form($form_info, $form_state);
  335. if (!empty($form_state['cancel'])) {
  336. $output = array(ctools_modal_command_dismiss());
  337. }
  338. else if (!empty($form_state['complete'])) {
  339. // Successful submit -- move temporary data to location.
  340. // Create a reference to the place our context lives. Since this is fairly
  341. // generic, this is the easiest way to get right to the place of the
  342. // object without knowing precisely what data we're poking at.
  343. $ref = &$object->{$type_info['key']};
  344. // Figure out the position for our new context.
  345. $position = empty($ref) ? 0 : max(array_keys($ref)) + 1;
  346. $conf['id'] = ctools_context_next_id($ref, $name);
  347. $ref[$position] = $conf;
  348. if (isset($object->temporary)) {
  349. unset($object->temporary);
  350. }
  351. ctools_cache_operation($mechanism, $cache_key, 'finalize', $object);
  352. // Very irritating way to update the form for our contexts.
  353. $arg_form_state = form_state_defaults() + array(
  354. 'values' => array(),
  355. 'process_input' => FALSE,
  356. 'complete form' => array(),
  357. );
  358. $rel_form_state = $arg_form_state;
  359. $arg_form = array(
  360. '#post' => array(),
  361. '#programmed' => FALSE,
  362. '#tree' => FALSE,
  363. '#parents' => array(),
  364. '#array_parents' => array(),
  365. );
  366. // Build a chunk of the form to merge into the displayed form
  367. $arg_form[$type] = array(
  368. '#tree' => TRUE,
  369. );
  370. $arg_form[$type][$position] = array(
  371. '#tree' => TRUE,
  372. );
  373. ctools_context_add_item_to_form($mechanism, $type, $cache_key, $arg_form[$type][$position], $position, $ref[$position]);
  374. $arg_form = form_builder('ctools_context_form', $arg_form, $arg_form_state);
  375. // Build the relationships table so we can ajax it in.
  376. // This is an additional thing that goes in here.
  377. $rel_form = array(
  378. '#theme' => 'ctools_context_item_form',
  379. '#cache_key' => $cache_key,
  380. '#ctools_context_type' => 'relationship',
  381. '#ctools_context_module' => $mechanism,
  382. '#only_buttons' => TRUE,
  383. '#post' => array(),
  384. '#programmed' => FALSE,
  385. '#tree' => FALSE,
  386. '#parents' => array(),
  387. '#array_parents' => array(),
  388. );
  389. $rel_form['relationship'] = array(
  390. '#tree' => TRUE,
  391. );
  392. // Allow an object to set some 'base' contexts that come from elsewhere.
  393. $rel_contexts = isset($object->base_contexts) ? $object->base_contexts : array();
  394. $all_contexts = ctools_context_load_contexts($object, TRUE, $rel_contexts);
  395. $available_relationships = ctools_context_get_relevant_relationships($all_contexts);
  396. $output = array();
  397. if (!empty($available_relationships)) {
  398. ctools_context_add_item_table_buttons('relationship', $mechanism, $rel_form, $available_relationships);
  399. $rel_form = form_builder('dummy_form_id', $rel_form, $rel_form_state);
  400. $output[] = ajax_command_replace('div#ctools-relationships-table div.buttons', drupal_render($rel_form));
  401. }
  402. $theme_vars = array();
  403. $theme_vars['type'] = $type;
  404. $theme_vars['form'] = $arg_form[$type][$position];
  405. $theme_vars['position'] = $position;
  406. $theme_vars['count'] = $position;
  407. $text = theme('ctools_context_item_row', $theme_vars);
  408. $output[] = ajax_command_append('#' . $type . '-table tbody', $text);
  409. $output[] = ajax_command_changed('#' . $type . '-row-' . $position, '.title');
  410. $output[] = ctools_modal_command_dismiss();
  411. }
  412. else {
  413. $output = ctools_modal_form_render($form_state, $output);
  414. }
  415. print ajax_render($output);
  416. exit;
  417. }
  418. /**
  419. * Ajax entry point to edit an item
  420. */
  421. function ctools_context_ajax_item_edit($mechanism = NULL, $type = NULL, $cache_key = NULL, $position = NULL, $step = NULL) {
  422. ctools_include('ajax');
  423. ctools_include('modal');
  424. ctools_include('context');
  425. ctools_include('cache');
  426. ctools_include('plugins-admin');
  427. if (!isset($position)) {
  428. return ctools_ajax_render_error();
  429. }
  430. // Load stored object from cache.
  431. if (!($object = ctools_cache_get($mechanism, $cache_key))) {
  432. ctools_ajax_render_error(t('Invalid object name.'));
  433. }
  434. $type_info = ctools_context_info($type);
  435. // Create a reference to the place our context lives. Since this is fairly
  436. // generic, this is the easiest way to get right to the place of the
  437. // object without knowing precisely what data we're poking at.
  438. $ref = &$object->{$type_info['key']};
  439. if (empty($step) || empty($object->temporary)) {
  440. // Create the basis for our new context.
  441. $conf = $object->{$type_info['key']}[$position];
  442. $object->temporary = &$conf;
  443. }
  444. else {
  445. $conf = &$object->temporary;
  446. }
  447. $name = $ref[$position]['name'];
  448. if (empty($name)) {
  449. ctools_ajax_render_error();
  450. }
  451. // load the plugin definition
  452. $plugin_definition = ctools_context_get_plugin($type, $name);
  453. if (empty($plugin_definition)) {
  454. ctools_ajax_render_error(t('Invalid context type'));
  455. }
  456. // Load the contexts
  457. $base_contexts = isset($object->base_contexts) ? $object->base_contexts : array();
  458. $contexts = ctools_context_load_contexts($object, TRUE, $base_contexts);
  459. $form_state = array(
  460. 'ajax' => TRUE,
  461. 'modal' => TRUE,
  462. 'modal return' => TRUE,
  463. 'object' => &$object,
  464. 'conf' => &$conf,
  465. 'position' => $position,
  466. 'plugin' => $plugin_definition,
  467. 'type' => $type,
  468. 'contexts' => $contexts,
  469. 'title' => t('Edit @type "@context"', array('@type' => $type_info['singular title'], '@context' => $plugin_definition['title'])),
  470. 'type info' => $type_info,
  471. 'op' => 'add',
  472. 'step' => $step,
  473. );
  474. $form_info = array(
  475. 'path' => "ctools/context/ajax/configure/$mechanism/$type/$cache_key/$position/%step",
  476. 'show cancel' => TRUE,
  477. 'default form' => 'ctools_edit_context_form_defaults',
  478. 'auto caching' => TRUE,
  479. 'cache mechanism' => $mechanism,
  480. 'cache key' => $cache_key,
  481. // This is stating what the cache will be referred to in $form_state
  482. 'cache storage' => 'object',
  483. );
  484. if ($type == 'requiredcontext') {
  485. $form_info += array(
  486. 'add form name' => 'required context add form',
  487. 'edit form name' => 'required context edit form',
  488. );
  489. }
  490. $output = ctools_plugin_configure_form($form_info, $form_state);
  491. if (!empty($form_state['cancel'])) {
  492. $output = array(ctools_modal_command_dismiss());
  493. }
  494. else if (!empty($form_state['complete'])) {
  495. // successful submit
  496. $ref[$position] = $conf;
  497. if (isset($object->temporary)) {
  498. unset($object->temporary);
  499. }
  500. ctools_cache_operation($mechanism, $cache_key, 'finalize', $object);
  501. $output = array();
  502. $output[] = ctools_modal_command_dismiss();
  503. $arg_form_state = form_state_defaults() + array(
  504. 'values' => array(),
  505. 'process_input' => FALSE,
  506. 'complete form' => array(),
  507. );
  508. $arg_form = array(
  509. '#post' => array(),
  510. '#parents' => array(),
  511. '#array_parents' => array(),
  512. '#programmed' => FALSE,
  513. '#tree' => FALSE,
  514. );
  515. // Build a chunk of the form to merge into the displayed form
  516. $arg_form[$type] = array(
  517. '#tree' => TRUE,
  518. );
  519. $arg_form[$type][$position] = array(
  520. '#tree' => TRUE,
  521. );
  522. ctools_context_add_item_to_form($mechanism, $type, $cache_key, $arg_form[$type][$position], $position, $ref[$position]);
  523. $arg_form = form_builder('ctools_context_form', $arg_form, $arg_form_state);
  524. $theme_vars = array();
  525. $theme_vars['type'] = $type;
  526. $theme_vars['form'] = $arg_form[$type][$position];
  527. $theme_vars['position'] = $position;
  528. $theme_vars['count'] = $position;
  529. $output[] = ajax_command_replace('#' . $type . '-row-' . $position, theme('ctools_context_item_row', $theme_vars));
  530. $output[] = ajax_command_changed('#' . $type . '-row-' . $position, '.title');
  531. }
  532. else {
  533. $output = ctools_modal_form_render($form_state, $output);
  534. }
  535. print ajax_render($output);
  536. exit;
  537. }
  538. /**
  539. * Get the defaults for a new instance of a context plugin.
  540. *
  541. * @param $plugin_definition
  542. * The metadata definition of the plugin from ctools_get_plugins().
  543. * @param $object
  544. * The object the context plugin will be added to.
  545. * @param $type
  546. * The type of context plugin. i.e, context, requiredcontext, relationship
  547. */
  548. function ctools_context_get_defaults($plugin_definition, $object, $type) {
  549. // Fetch the potential id of the plugin so we can append
  550. // title and keyword information for new ones.
  551. $type_info = ctools_context_info($type);
  552. $id = ctools_context_next_id($object->{$type_info['key']}, $plugin_definition['name']);
  553. $conf = array(
  554. 'identifier' => $plugin_definition['title'] . ($id > 1 ? ' ' . $id : ''),
  555. 'keyword' => ctools_get_keyword($object, $plugin_definition['keyword']),
  556. 'name' => $plugin_definition['name'],
  557. );
  558. if (isset($plugin_definition['defaults'])) {
  559. $defaults = $plugin_definition['defaults'];
  560. }
  561. else if (isset($subtype['defaults'])) {
  562. $defaults = $subtype['defaults'];
  563. }
  564. if (isset($defaults)) {
  565. if (is_string($defaults) && function_exists($defaults)) {
  566. if ($settings = $defaults($plugin_definition)) {
  567. $conf += $settings;
  568. }
  569. }
  570. else if (is_array($defaults)) {
  571. $conf += $defaults;
  572. }
  573. }
  574. return $conf;
  575. }
  576. /**
  577. * Form wrapper for the edit context form.
  578. *
  579. * @todo: We should uncombine these.
  580. */
  581. function ctools_edit_context_form_defaults($form, &$form_state) {
  582. // Basic values required to orient ourselves
  583. $object = $form_state['object'];
  584. $plugin_definition = $form_state['plugin'];
  585. $type_info = $form_state['type info'];
  586. $contexts = $form_state['contexts'];
  587. $conf = $form_state['conf'];
  588. if ($type_info['key'] == 'arguments' && !isset($conf['default'])) {
  589. $conf['default'] = 'ignore';
  590. $conf['title'] = '';
  591. }
  592. $form['description'] = array(
  593. '#prefix' => '<div class="description">',
  594. '#suffix' => '</div>',
  595. '#markup' => check_plain($plugin_definition['description']),
  596. );
  597. if ($type_info['key'] == 'relationships') {
  598. $form['context'] = ctools_context_selector($contexts, $plugin_definition['required context'], isset($conf['context']) ? $conf['context'] : '');
  599. }
  600. if ($type_info['key'] == 'arguments') {
  601. $form['default'] = array(
  602. '#type' => 'select',
  603. '#title' => t('Default'),
  604. '#options' => array(
  605. 'ignore' => t('Ignore it; content that requires this context will not be available.'),
  606. '404' => t('Display page not found or display nothing at all.'),
  607. ),
  608. '#default_value' => $conf['default'],
  609. '#description' => t('If the argument is missing or is not valid, select how this should behave.'),
  610. );
  611. $form['title'] = array(
  612. '#type' => 'textfield',
  613. '#title' => t('Title'),
  614. '#default_value' => $conf['title'],
  615. '#description' => t('Enter a title to use when this argument is present. You may use %KEYWORD substitution, where the keyword is specified below.'),
  616. );
  617. }
  618. $form['identifier'] = array(
  619. '#type' => 'textfield',
  620. '#title' => t('Identifier'),
  621. '#description' => t('Enter a name to identify this !type on administrative screens.', array('!type' => t('context'))),
  622. '#default_value' => $conf['identifier'],
  623. );
  624. $form['keyword'] = array(
  625. '#type' => 'textfield',
  626. '#title' => t('Keyword'),
  627. '#description' => t('Enter a keyword to use for substitution in titles.'),
  628. '#default_value' => $conf['keyword'],
  629. );
  630. $form['#submit'][] = 'ctools_edit_context_form_defaults_submit';
  631. return $form;
  632. }
  633. /**
  634. * Submit handler to store context identifier and keyword info.
  635. */
  636. function ctools_edit_context_form_defaults_submit(&$form, &$form_state) {
  637. if ($form_state['type info']['key'] == 'relationships') {
  638. $form_state['conf']['context'] = $form_state['values']['context'];
  639. }
  640. if ($form_state['type info']['key'] == 'arguments') {
  641. $form_state['conf']['default'] = $form_state['values']['default'];
  642. $form_state['conf']['title'] = $form_state['values']['title'];
  643. }
  644. $form_state['conf']['identifier'] = $form_state['values']['identifier'];
  645. $form_state['conf']['keyword'] = $form_state['values']['keyword'];
  646. }
  647. /**
  648. * Ajax entry point to edit an item
  649. */
  650. function ctools_context_ajax_item_delete($mechanism = NULL, $type = NULL, $cache_key = NULL, $position = NULL) {
  651. ctools_include('ajax');
  652. ctools_include('context');
  653. ctools_include('cache');
  654. if (!isset($position)) {
  655. return ctools_ajax_render_error();
  656. }
  657. // Load stored object from cache.
  658. if (!($object = ctools_cache_get($mechanism, $cache_key))) {
  659. ctools_ajax_render_error(t('Invalid object name.'));
  660. }
  661. $type_info = ctools_context_info($type);
  662. // Create a reference to the place our context lives. Since this is fairly
  663. // generic, this is the easiest way to get right to the place of the
  664. // object without knowing precisely what data we're poking at.
  665. $ref = &$object->{$type_info['key']};
  666. if (!array_key_exists($position, $ref)) {
  667. ctools_ajax_render_error(t('Unable to delete missing item!'));
  668. }
  669. unset($ref[$position]);
  670. ctools_cache_operation($mechanism, $cache_key, 'finalize', $object);
  671. $output = array();
  672. $output[] = ajax_command_replace('#' . $type . '-row-' . $position, '');
  673. $output[] = ajax_command_restripe("#$type-table");
  674. print ajax_render($output);
  675. exit;
  676. }
  677. // --- End of contexts
  678. function ctools_save_context($type, &$ref, $form_values) {
  679. $type_info = ctools_context_info($type);
  680. // Organize arguments
  681. $new = array();
  682. $order = array();
  683. foreach ($ref as $id => $context) {
  684. $position = $form_values[$type][$id]['position'];
  685. $order[$position] = $id;
  686. }
  687. ksort($order);
  688. foreach ($order as $id) {
  689. $new[] = $ref[$id];
  690. }
  691. $ref = $new;
  692. }
  693. function ctools_get_keyword($page, $word) {
  694. // Create a complete set of keywords
  695. $keywords = array();
  696. foreach (array('arguments', 'relationships', 'contexts', 'requiredcontexts') as $type) {
  697. if (!empty($page->$type) && is_array($page->$type)) {
  698. foreach ($page->$type as $info) {
  699. $keywords[$info['keyword']] = TRUE;
  700. }
  701. }
  702. }
  703. $keyword = $word;
  704. $count = 1;
  705. while (!empty($keywords[$keyword])) {
  706. $keyword = $word . '_' . ++$count;
  707. }
  708. return $keyword;
  709. }