content.inc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. <?php
  2. /**
  3. * @file
  4. * Contains the tools to handle pluggable content that can be used by other
  5. * applications such as Panels or Dashboard.
  6. *
  7. * See the context-content.html file in advanced help for documentation
  8. * of this tool.
  9. */
  10. /**
  11. * Provide defaults for a content type.
  12. *
  13. * Currently we check for automatically named callbacks to make life a little
  14. * easier on the developer.
  15. */
  16. function ctools_content_process(&$plugin, $info) {
  17. $function_base = $plugin['module'] . '_' . $plugin['name'] . '_content_type_';
  18. if (empty($plugin['render callback']) && function_exists($function_base . 'render')) {
  19. $plugin['render callback'] = $function_base . 'render';
  20. }
  21. if (empty($plugin['admin title'])) {
  22. if (function_exists($function_base . 'admin_title')) {
  23. $plugin['admin title'] = $function_base . 'admin_title';
  24. }
  25. else {
  26. $plugin['admin title'] = $plugin['title'];
  27. }
  28. }
  29. if (empty($plugin['admin info']) && function_exists($function_base . 'admin_info')) {
  30. $plugin['admin info'] = $function_base . 'admin_info';
  31. }
  32. if (!isset($plugin['edit form']) && function_exists($function_base . 'edit_form')) {
  33. $plugin['edit form'] = $function_base . 'edit_form';
  34. }
  35. if (!isset($plugin['add form']) && function_exists($function_base . 'add_form')) {
  36. $plugin['add form'] = $function_base . 'add_form';
  37. }
  38. if (!isset($plugin['add form']) && function_exists($function_base . 'edit_form')) {
  39. $plugin['add form'] = $function_base . 'edit_form';
  40. }
  41. if (!isset($plugin['description'])) {
  42. $plugin['description'] = '';
  43. }
  44. if (!isset($plugin['icon'])) {
  45. $plugin['icon'] = ctools_content_admin_icon($plugin);
  46. }
  47. // Another ease of use check:
  48. if (!isset($plugin['content types'])) {
  49. // If a subtype plugin exists, try to use it. Otherwise assume single.
  50. if (function_exists($function_base . 'content_types')) {
  51. $plugin['content types'] = $function_base . 'content_types';
  52. }
  53. else {
  54. $type = array(
  55. 'title' => $plugin['title'],
  56. 'description' => $plugin['description'],
  57. 'icon' => ctools_content_admin_icon($plugin),
  58. 'category' => $plugin['category'],
  59. );
  60. if (isset($plugin['required context'])) {
  61. $type['required context'] = $plugin['required context'];
  62. }
  63. if (isset($plugin['top level'])) {
  64. $type['top level'] = $plugin['top level'];
  65. }
  66. $plugin['content types'] = array($plugin['name'] => $type);
  67. if (!isset($plugin['single'])) {
  68. $plugin['single'] = TRUE;
  69. }
  70. }
  71. }
  72. }
  73. /**
  74. * Fetch metadata on a specific content_type plugin.
  75. *
  76. * @param mixed $content
  77. * Name of a panel content type.
  78. *
  79. * @return
  80. * An array with information about the requested panel content type.
  81. */
  82. function ctools_get_content_type($content_type) {
  83. ctools_include('context');
  84. ctools_include('plugins');
  85. return ctools_get_plugins('ctools', 'content_types', $content_type);
  86. }
  87. /**
  88. * Fetch metadata for all content_type plugins.
  89. *
  90. * @return
  91. * An array of arrays with information about all available panel content types.
  92. */
  93. function ctools_get_content_types() {
  94. ctools_include('context');
  95. ctools_include('plugins');
  96. return ctools_get_plugins('ctools', 'content_types');
  97. }
  98. /**
  99. * Get all of the individual subtypes provided by a given content type. This
  100. * would be all of the blocks for the block type, or all of the views for
  101. * the view type.
  102. *
  103. * @param $type
  104. * The content type to load.
  105. *
  106. * @return
  107. * An array of all subtypes available.
  108. */
  109. function ctools_content_get_subtypes($type) {
  110. static $cache = array();
  111. $subtypes = array();
  112. if (is_array($type)) {
  113. $plugin = $type;
  114. }
  115. else {
  116. $plugin = ctools_get_content_type($type);
  117. }
  118. if (empty($plugin) || empty($plugin['name'])) {
  119. return;
  120. }
  121. if (isset($cache[$plugin['name']])) {
  122. return $cache[$plugin['name']];
  123. }
  124. if (isset($plugin['content types'])) {
  125. $function = $plugin['content types'];
  126. if (is_array($function)) {
  127. $subtypes = $function;
  128. }
  129. elseif (function_exists($function)) {
  130. // Cast to array to prevent errors from non-array returns.
  131. $subtypes = (array) $function($plugin);
  132. }
  133. }
  134. // Walk through the subtypes and ensure minimal settings are
  135. // retained.
  136. foreach ($subtypes as $id => $subtype) {
  137. // Ensure that the 'subtype_id' value exists.
  138. if (!isset($subtype['subtype_id'])) {
  139. $subtypes[$id]['subtype_id'] = $id;
  140. }
  141. // Use exact name since this is a modify by reference.
  142. ctools_content_prepare_subtype($subtypes[$id], $plugin);
  143. }
  144. $cache[$plugin['name']] = $subtypes;
  145. return $subtypes;
  146. }
  147. /**
  148. * Given a content type and a subtype id, return the information about that
  149. * content subtype.
  150. *
  151. * @param $type
  152. * The content type being fetched.
  153. * @param $subtype_id
  154. * The id of the subtype being fetched.
  155. *
  156. * @return
  157. * An array of information describing the content subtype.
  158. */
  159. function ctools_content_get_subtype($type, $subtype_id) {
  160. $subtype = array();
  161. if (is_array($type)) {
  162. $plugin = $type;
  163. }
  164. else {
  165. $plugin = ctools_get_content_type($type);
  166. }
  167. $function = ctools_plugin_get_function($plugin, 'content type');
  168. if ($function) {
  169. $subtype = $function($subtype_id, $plugin);
  170. }
  171. else {
  172. $subtypes = ctools_content_get_subtypes($type);
  173. if (isset($subtypes[$subtype_id])) {
  174. $subtype = $subtypes[$subtype_id];
  175. }
  176. // If there's only 1 and we somehow have the wrong subtype ID, do not
  177. // care. Return the proper subtype anyway.
  178. if (empty($subtype) && !empty($plugin['single'])) {
  179. $subtype = current($subtypes);
  180. }
  181. }
  182. if ($subtype) {
  183. // Ensure that the 'subtype_id' value exists. This is also done in
  184. // ctools_content_get_subtypes(), but it wouldn't be called if the plugin
  185. // provides the subtype through its own function.
  186. if (!isset($subtype['subtype_id'])) {
  187. $subtype['subtype_id'] = $subtype_id;
  188. }
  189. ctools_content_prepare_subtype($subtype, $plugin);
  190. }
  191. return $subtype;
  192. }
  193. /**
  194. * Ensure minimal required settings on a content subtype exist.
  195. */
  196. function ctools_content_prepare_subtype(&$subtype, $plugin) {
  197. foreach (array('path', 'js', 'css') as $key) {
  198. if (!isset($subtype[$key]) && isset($plugin[$key])) {
  199. $subtype[$key] = $plugin[$key];
  200. }
  201. }
  202. // Trigger hook_ctools_content_subtype_alter().
  203. drupal_alter('ctools_content_subtype', $subtype, $plugin);
  204. }
  205. /**
  206. * Get the content from a given content type.
  207. *
  208. * @param $type
  209. * The content type. May be the name or an already loaded content type plugin.
  210. * @param $subtype
  211. * The name of the subtype being rendered.
  212. * @param $conf
  213. * The configuration for the content type.
  214. * @param $keywords
  215. * An array of replacement keywords that come from outside contexts.
  216. * @param $args
  217. * The arguments provided to the owner of the content type. Some content may
  218. * wish to configure itself based on the arguments the panel or dashboard
  219. * received.
  220. * @param $context
  221. * An array of context objects available for use.
  222. * @param $incoming_content
  223. * Any incoming content, if this display is a wrapper.
  224. *
  225. * @return
  226. * The content as rendered by the plugin, or NULL.
  227. * This content should be an object with the following possible properties:
  228. * - title: The safe to render title of the content.
  229. * - title_heading: The title heading.
  230. * - content: The safe to render HTML content.
  231. * - links: An array of links associated with the content suitable for
  232. * theme('links').
  233. * - more: An optional 'more' link (destination only)
  234. * - admin_links: Administrative links associated with the content, suitable
  235. * for theme('links').
  236. * - feeds: An array of feed icons or links associated with the content.
  237. * Each member of the array is rendered HTML.
  238. * - type: The content type.
  239. * - subtype: The content subtype. These two may be used together as
  240. * module-delta for block style rendering.
  241. */
  242. function ctools_content_render($type, $subtype, $conf, $keywords = array(), $args = array(), $context = array(), $incoming_content = '') {
  243. if (is_array($type)) {
  244. $plugin = $type;
  245. }
  246. else {
  247. $plugin = ctools_get_content_type($type);
  248. }
  249. $subtype_info = ctools_content_get_subtype($plugin, $subtype);
  250. $function = ctools_plugin_get_function($subtype_info, 'render callback');
  251. if (!$function) {
  252. $function = ctools_plugin_get_function($plugin, 'render callback');
  253. }
  254. if ($function) {
  255. $pane_context = ctools_content_select_context($plugin, $subtype, $conf, $context);
  256. if ($pane_context === FALSE) {
  257. return;
  258. }
  259. $content = $function($subtype, $conf, $args, $pane_context, $incoming_content);
  260. if (empty($content)) {
  261. return;
  262. }
  263. // Set up some defaults and other massaging on the content before we hand
  264. // it back to the caller.
  265. if (!isset($content->type)) {
  266. $content->type = $plugin['name'];
  267. }
  268. if (!isset($content->subtype)) {
  269. $content->subtype = $subtype;
  270. }
  271. // Override the title if configured to.
  272. if (!empty($conf['override_title'])) {
  273. // Give previous title as an available substitution here.
  274. $keywords['%title'] = empty($content->title) ? '' : $content->title;
  275. $content->original_title = $keywords['%title'];
  276. $content->title = $conf['override_title_text'];
  277. $content->title_heading = isset($conf['override_title_heading']) ? $conf['override_title_heading'] : 'h2';
  278. }
  279. if (!empty($content->title)) {
  280. // Perform substitutions.
  281. if (!empty($keywords) || !empty($context)) {
  282. $content->title = ctools_context_keyword_substitute($content->title, $keywords, $context);
  283. }
  284. // Sterilize the title.
  285. $content->title = filter_xss_admin($content->title);
  286. // If a link is specified, populate.
  287. if (!empty($content->title_link)) {
  288. if (!is_array($content->title_link)) {
  289. $url = array('href' => $content->title_link);
  290. }
  291. else {
  292. $url = $content->title_link;
  293. }
  294. // Set defaults so we don't bring up notices.
  295. $url += array('href' => '', 'attributes' => array(), 'query' => array(), 'fragment' => '', 'absolute' => NULL, 'html' => TRUE);
  296. $content->title = l($content->title, $url['href'], $url);
  297. }
  298. }
  299. return $content;
  300. }
  301. }
  302. /**
  303. * Determine if a content type can be edited or not.
  304. *
  305. * Some content types simply have their content and no options. This function
  306. * lets a UI determine if it should display an edit link or not.
  307. */
  308. function ctools_content_editable($type, $subtype, $conf) {
  309. if (empty($type['edit form']) && empty($subtype['edit form'])) {
  310. return FALSE;
  311. }
  312. $function = FALSE;
  313. if (!empty($subtype['check editable'])) {
  314. $function = ctools_plugin_get_function($subtype, 'check editable');
  315. }
  316. elseif (!empty($type['check editable'])) {
  317. $function = ctools_plugin_get_function($type, 'check editable');
  318. }
  319. if ($function) {
  320. return $function($type, $subtype, $conf);
  321. }
  322. return TRUE;
  323. }
  324. /**
  325. * Get the administrative title from a given content type.
  326. *
  327. * @param $type
  328. * The content type. May be the name or an already loaded content type object.
  329. * @param $subtype
  330. * The subtype being rendered.
  331. * @param $conf
  332. * The configuration for the content type.
  333. * @param $context
  334. * An array of context objects available for use. These may be placeholders.
  335. */
  336. function ctools_content_admin_title($type, $subtype, $conf, $context = NULL) {
  337. if (is_array($type)) {
  338. $plugin = $type;
  339. }
  340. elseif (is_string($type)) {
  341. $plugin = ctools_get_content_type($type);
  342. }
  343. else {
  344. return;
  345. }
  346. if ($function = ctools_plugin_get_function($plugin, 'admin title')) {
  347. $pane_context = ctools_content_select_context($plugin, $subtype, $conf, $context);
  348. if ($pane_context === FALSE) {
  349. if ($plugin['name'] == $subtype) {
  350. return t('@type will not display due to missing context', array('@type' => $plugin['name']));
  351. }
  352. return t('@type:@subtype will not display due to missing context', array('@type' => $plugin['name'], '@subtype' => $subtype));
  353. }
  354. return $function($subtype, $conf, $pane_context);
  355. }
  356. elseif (isset($plugin['admin title'])) {
  357. return $plugin['admin title'];
  358. }
  359. elseif (isset($plugin['title'])) {
  360. return $plugin['title'];
  361. }
  362. }
  363. /**
  364. * Get the proper icon path to use, falling back to default icons if no icon exists.
  365. *
  366. * $subtype
  367. * The loaded subtype info.
  368. */
  369. function ctools_content_admin_icon($subtype) {
  370. $icon = '';
  371. if (isset($subtype['icon'])) {
  372. $icon = $subtype['icon'];
  373. if (!file_exists($icon)) {
  374. $icon = $subtype['path'] . '/' . $icon;
  375. }
  376. }
  377. if (empty($icon) || !file_exists($icon)) {
  378. $icon = ctools_image_path('no-icon.png');
  379. }
  380. return $icon;
  381. }
  382. /**
  383. * Set up the default $conf for a new instance of a content type.
  384. */
  385. function ctools_content_get_defaults($plugin, $subtype) {
  386. if (isset($plugin['defaults'])) {
  387. $defaults = $plugin['defaults'];
  388. }
  389. elseif (isset($subtype['defaults'])) {
  390. $defaults = $subtype['defaults'];
  391. }
  392. if (isset($defaults)) {
  393. if (is_string($defaults) && function_exists($defaults)) {
  394. if ($return = $defaults($pane)) {
  395. return $return;
  396. }
  397. }
  398. elseif (is_array($defaults)) {
  399. return $defaults;
  400. }
  401. }
  402. return array();
  403. }
  404. /**
  405. * Get the administrative title from a given content type.
  406. *
  407. * @param $type
  408. * The content type. May be the name or an already loaded content type object.
  409. * @param $subtype
  410. * The subtype being rendered.
  411. * @param $conf
  412. * The configuration for the content type.
  413. * @param $context
  414. * An array of context objects available for use. These may be placeholders.
  415. */
  416. function ctools_content_admin_info($type, $subtype, $conf, $context = NULL) {
  417. if (is_array($type)) {
  418. $plugin = $type;
  419. }
  420. else {
  421. $plugin = ctools_get_content_type($type);
  422. }
  423. if ($function = ctools_plugin_get_function($plugin, 'admin info')) {
  424. $output = $function($subtype, $conf, $context);
  425. }
  426. if (empty($output) || !is_object($output)) {
  427. $output = new stdClass();
  428. // Replace the _ with " " for a better output.
  429. $subtype = check_plain(str_replace("_", " ", $subtype));
  430. $output->title = $subtype;
  431. $output->content = t('No info available.');
  432. }
  433. return $output;
  434. }
  435. /**
  436. * Add the default FAPI elements to the content type configuration form.
  437. */
  438. function ctools_content_configure_form_defaults($form, &$form_state) {
  439. $plugin = $form_state['plugin'];
  440. $subtype = $form_state['subtype'];
  441. $contexts = isset($form_state['contexts']) ? $form_state['contexts'] : NULL;
  442. $conf = $form_state['conf'];
  443. $add_submit = FALSE;
  444. if (!empty($subtype['required context']) && is_array($contexts)) {
  445. $form['context'] = ctools_context_selector($contexts, $subtype['required context'], isset($conf['context']) ? $conf['context'] : array());
  446. $add_submit = TRUE;
  447. }
  448. ctools_include('dependent');
  449. // Unless we're not allowed to override the title on this content type, add this
  450. // gadget to all panes.
  451. if (empty($plugin['no title override']) && empty($subtype['no title override'])) {
  452. $form['aligner_start'] = array(
  453. '#markup' => '<div class="option-text-aligner clearfix">',
  454. );
  455. $form['override_title'] = array(
  456. '#type' => 'checkbox',
  457. '#default_value' => isset($conf['override_title']) ? $conf['override_title'] : '',
  458. '#title' => t('Override title'),
  459. '#id' => 'override-title-checkbox',
  460. );
  461. $form['override_title_text'] = array(
  462. '#type' => 'textfield',
  463. '#default_value' => isset($conf['override_title_text']) ? $conf['override_title_text'] : '',
  464. '#size' => 35,
  465. '#id' => 'override-title-textfield',
  466. '#dependency' => array('override-title-checkbox' => array(1)),
  467. '#dependency_type' => 'hidden',
  468. );
  469. $form['override_title_heading'] = array(
  470. '#type' => 'select',
  471. '#default_value' => isset($conf['override_title_heading']) ? $conf['override_title_heading'] : 'h2',
  472. '#options' => array(
  473. 'h1' => t('h1'),
  474. 'h2' => t('h2'),
  475. 'h3' => t('h3'),
  476. 'h4' => t('h4'),
  477. 'h5' => t('h5'),
  478. 'h6' => t('h6'),
  479. 'div' => t('div'),
  480. 'span' => t('span'),
  481. ),
  482. '#id' => 'override-title-heading',
  483. '#dependency' => array('override-title-checkbox' => array(1)),
  484. '#dependency_type' => 'hidden',
  485. );
  486. $form['aligner_stop'] = array(
  487. '#markup' => '</div>',
  488. );
  489. if (is_array($contexts)) {
  490. $form['override_title_markup'] = array(
  491. '#prefix' => '<div class="description">',
  492. '#suffix' => '</div>',
  493. '#markup' => t('You may use %keywords from contexts, as well as %title to contain the original title.'),
  494. );
  495. }
  496. $add_submit = TRUE;
  497. }
  498. if ($add_submit) {
  499. // '#submit' is already set up due to the wizard.
  500. $form['#submit'][] = 'ctools_content_configure_form_defaults_submit';
  501. }
  502. return $form;
  503. }
  504. /**
  505. * Submit handler to store context/title override info.
  506. */
  507. function ctools_content_configure_form_defaults_submit(&$form, &$form_state) {
  508. if (isset($form_state['values']['context'])) {
  509. $form_state['conf']['context'] = $form_state['values']['context'];
  510. }
  511. if (isset($form_state['values']['override_title'])) {
  512. $form_state['conf']['override_title'] = $form_state['values']['override_title'];
  513. $form_state['conf']['override_title_text'] = $form_state['values']['override_title_text'];
  514. $form_state['conf']['override_title_heading'] = $form_state['values']['override_title_heading'];
  515. }
  516. }
  517. /**
  518. * Get the config form.
  519. *
  520. * The $form_info and $form_state need to be preconfigured with data you'll need
  521. * such as whether or not you're using ajax, or the modal. $form_info will need
  522. * your next/submit callbacks so that you can cache your data appropriately.
  523. *
  524. * @return
  525. * If this function returns false, no form exists.
  526. */
  527. function ctools_content_form($op, $form_info, &$form_state, $plugin, $subtype_name, $subtype, &$conf, $step = NULL) {
  528. $form_state += array(
  529. 'plugin' => $plugin,
  530. 'subtype' => $subtype,
  531. 'subtype_name' => $subtype_name,
  532. 'conf' => &$conf,
  533. 'op' => $op,
  534. );
  535. $form_info += array(
  536. 'id' => 'ctools_content_form',
  537. 'show back' => TRUE,
  538. );
  539. // Turn the forms defined in the plugin into the format the wizard needs.
  540. if ($op == 'add') {
  541. if (!empty($subtype['add form'])) {
  542. _ctools_content_create_form_info($form_info, $subtype['add form'], $subtype, $subtype, $op);
  543. }
  544. elseif (!empty($plugin['add form'])) {
  545. _ctools_content_create_form_info($form_info, $plugin['add form'], $plugin, $subtype, $op);
  546. }
  547. }
  548. if (empty($form_info['order'])) {
  549. // Use the edit form for the add form if add form was completely left off.
  550. if (!empty($subtype['edit form'])) {
  551. _ctools_content_create_form_info($form_info, $subtype['edit form'], $subtype, $subtype, $op);
  552. }
  553. elseif (!empty($plugin['edit form'])) {
  554. _ctools_content_create_form_info($form_info, $plugin['edit form'], $plugin, $subtype, $op);
  555. }
  556. }
  557. if (empty($form_info['order'])) {
  558. return FALSE;
  559. }
  560. ctools_include('wizard');
  561. return ctools_wizard_multistep_form($form_info, $step, $form_state);
  562. }
  563. function _ctools_content_create_form_info(&$form_info, $info, $plugin, $subtype, $op) {
  564. if (is_string($info)) {
  565. if (empty($subtype['title'])) {
  566. $title = t('Configure');
  567. }
  568. elseif ($op == 'add') {
  569. $title = t('Configure new !subtype_title', array('!subtype_title' => $subtype['title']));
  570. }
  571. else {
  572. $title = t('Configure !subtype_title', array('!subtype_title' => $subtype['title']));
  573. }
  574. $form_info['order'] = array('form' => $title);
  575. $form_info['forms'] = array(
  576. 'form' => array(
  577. 'title' => $title,
  578. 'form id' => $info,
  579. 'wrapper' => 'ctools_content_configure_form_defaults',
  580. ),
  581. );
  582. }
  583. elseif (is_array($info)) {
  584. $form_info['order'] = array();
  585. $form_info['forms'] = array();
  586. $count = 0;
  587. $base = 'step';
  588. $wrapper = NULL;
  589. foreach ($info as $form_id => $title) {
  590. // @todo -- docs say %title can be used to sub for the admin title.
  591. $step = $base . ++$count;
  592. if (empty($wrapper)) {
  593. $wrapper = $step;
  594. }
  595. if (is_array($title)) {
  596. if (!empty($title['default'])) {
  597. $wrapper = $step;
  598. }
  599. $title = $title['title'];
  600. }
  601. $form_info['order'][$step] = $title;
  602. $form_info['forms'][$step] = array(
  603. 'title' => $title,
  604. 'form id' => $form_id,
  605. );
  606. }
  607. if ($wrapper) {
  608. $form_info['forms'][$wrapper]['wrapper'] = 'ctools_content_configure_form_defaults';
  609. }
  610. }
  611. }
  612. /**
  613. * Get an array of all available content types that can be fed into the
  614. * display editor for the add content list.
  615. *
  616. * @param $context
  617. * If a context is provided, content that requires that context can apepar.
  618. * @param $has_content
  619. * Whether or not the display will have incoming content
  620. * @param $allowed_types
  621. * An array of allowed content types (pane types) keyed by content_type . '-' . sub_type
  622. * @param $default_types
  623. * A default allowed/denied status for content that isn't known about
  624. */
  625. function ctools_content_get_available_types($contexts = NULL, $has_content = FALSE, $allowed_types = NULL, $default_types = NULL) {
  626. $plugins = ctools_get_content_types();
  627. $available = array();
  628. foreach ($plugins as $id => $plugin) {
  629. foreach (ctools_content_get_subtypes($plugin) as $subtype_id => $subtype) {
  630. // Exclude items that require content if we're saying we don't
  631. // provide it.
  632. if (!empty($subtype['requires content']) && !$has_content) {
  633. continue;
  634. }
  635. // Check to see if the content type can be used in this context.
  636. if (!empty($subtype['required context'])) {
  637. if (!ctools_context_match_requirements($contexts, $subtype['required context'])) {
  638. continue;
  639. }
  640. }
  641. // Check to see if the passed-in allowed types allows this content.
  642. if ($allowed_types) {
  643. $key = $id . '-' . $subtype_id;
  644. if (!isset($allowed_types[$key])) {
  645. $allowed_types[$key] = isset($default_types[$id]) ? $default_types[$id] : $default_types['other'];
  646. }
  647. if (!$allowed_types[$key]) {
  648. continue;
  649. }
  650. }
  651. // Check if the content type provides an access callback.
  652. if (isset($subtype['create content access']) && function_exists($subtype['create content access']) && !$subtype['create content access']($plugin, $subtype)) {
  653. continue;
  654. }
  655. // If we made it through all the tests, then we can use this content.
  656. $available[$id][$subtype_id] = $subtype;
  657. }
  658. }
  659. return $available;
  660. }
  661. /**
  662. * Get an array of all content types that can be fed into the
  663. * display editor for the add content list, regardless of
  664. * availability.
  665. */
  666. function ctools_content_get_all_types() {
  667. $plugins = ctools_get_content_types();
  668. $available = array();
  669. foreach ($plugins as $id => $plugin) {
  670. foreach (ctools_content_get_subtypes($plugin) as $subtype_id => $subtype) {
  671. // If we made it through all the tests, then we can use this content.
  672. $available[$id][$subtype_id] = $subtype;
  673. }
  674. }
  675. return $available;
  676. }
  677. /**
  678. * Select the context to be used for a piece of content, based upon config.
  679. *
  680. * @param $plugin
  681. * The content plugin
  682. * @param $subtype
  683. * The subtype of the content.
  684. * @param $conf
  685. * The configuration array that should contain the context.
  686. * @param $contexts
  687. * A keyed array of available contexts.
  688. *
  689. * @return
  690. * The matching contexts or NULL if none or necessary, or FALSE if
  691. * requirements can't be met.
  692. */
  693. function ctools_content_select_context($plugin, $subtype, $conf, $contexts) {
  694. // Identify which of our possible contexts apply.
  695. if (empty($subtype)) {
  696. return;
  697. }
  698. $subtype_info = ctools_content_get_subtype($plugin, $subtype);
  699. if (empty($subtype_info)) {
  700. return;
  701. }
  702. if (!empty($subtype_info['all contexts']) || !empty($plugin['all contexts'])) {
  703. return $contexts;
  704. }
  705. // If the content requires a context, fetch it; if no context is returned,
  706. // do not display the pane.
  707. if (empty($subtype_info['required context'])) {
  708. return;
  709. }
  710. // Deal with dynamic required contexts not getting updated in the panes.
  711. // For example, Views let you dynamically change context info. While
  712. // we cannot be perfect, one thing we can do is if no context at all
  713. // was asked for, and then was later added but none is selected, make
  714. // a best guess as to what context should be used. THis is right more
  715. // than it's wrong.
  716. if (is_array($subtype_info['required context'])) {
  717. if (empty($conf['context']) || count($subtype_info['required context']) != count($conf['context'])) {
  718. foreach ($subtype_info['required context'] as $index => $required) {
  719. if (!isset($conf['context'][$index])) {
  720. $filtered = ctools_context_filter($contexts, $required);
  721. if ($filtered) {
  722. $keys = array_keys($filtered);
  723. $conf['context'][$index] = array_shift($keys);
  724. }
  725. }
  726. }
  727. }
  728. }
  729. else {
  730. if (empty($conf['context'])) {
  731. $filtered = ctools_context_filter($contexts, $subtype_info['required context']);
  732. if ($filtered) {
  733. $keys = array_keys($filtered);
  734. $conf['context'] = array_shift($keys);
  735. }
  736. }
  737. }
  738. if (empty($conf['context'])) {
  739. return;
  740. }
  741. $context = ctools_context_select($contexts, $subtype_info['required context'], $conf['context']);
  742. return $context;
  743. }
  744. /**
  745. * Fetch a piece of content from the addressable content system.
  746. *
  747. * @param $address
  748. * A string or an array representing the address of the content.
  749. * @param $type
  750. * The type of content to return. The type is dependent on what
  751. * the content actually is. The default, 'content' means a simple
  752. * string representing the content. However, richer systems may
  753. * offer more options. For example, Panels might allow the
  754. * fetching of 'display' and 'pane' objects. Page Manager
  755. * might allow for the fetching of 'task_handler' objects
  756. * (AKA variants).
  757. */
  758. function ctools_get_addressable_content($address, $type = 'content') {
  759. if (!is_array($address)) {
  760. $address = explode('::', $address);
  761. }
  762. if (!$address) {
  763. return;
  764. }
  765. // This removes the module from the address so the
  766. // implementor is not responsible for that part.
  767. $module = array_shift($address);
  768. return module_invoke($module, 'addressable_content', $address, $type);
  769. }