diff --git a/API.txt b/API.txt index a6589524f38c81d77986e261db16fdbdb6ade388..ac5488fe88842061771688cd0e3e73885de09a80 100644 --- a/API.txt +++ b/API.txt @@ -1,125 +1,4 @@ -The API for expanding the panels module comes in two pieces. First there is the -layout API, which adds to the list of layouts you need. Second is the content -types API, which lets modules supply content to the panels. Natively, panels -module supports the content types of 'block', which just renders the output -of a block, 'node' which simply renders a node_view, 'custom' which allows the -user to enter custom content with filtering, and finally 'views' because I -wrote them both. - -Where to put your code: -======================= - -With both types, there are two ways to implement a new type. First, you can -implement the hook in your module and provide the necessary data. Or you -can create a .inc file in the right format, and drop it into the proper -directory in the panels module. Both are very similar, and only requires -a minor naming adjustment. - -When using the .inc file, in place of 'hook' in the various hooks, use -panels_FILENAME. - -Creating a new Layout Type: -=========================== - -A layout consists of 4 things: - -1) A bit of HTML in a theme function. I use heredoc notation to make it - extra easy to convert these to .tpl.inc files in case they are to - be overridden in php template. -2) A bit of CSS to describe how the layout should be, well, laid out. -3) An icon that is 50x75 which gives the user a visual indication of - what the layout looks like. -4) An implementation of hook_panels_layouts() to tell panels the necessary - information. - -hook_panels_layouts returns an array with the following information: - -'module' => The module name providing this. This is necessary because it - uses drupal_get_path('module', $module) to get the proper - path for included CSS. -'title' => The title of the layout presented to the user. Use t(). -'icon' => The filename of the icon to use when listing avialable layouts. -'theme' => The theme function that contains the HTML, without the theme_ - part. -'css' => The CSS file. -'content areas' => an array in the form of 'name' => t('Title') of content - areas supported by the layout. For example, the simple - 2 column layout uses array('left' => t('Left side'), - 'right' => t('Right side')); -- the name is the internal - identifier. Your theme function will see it as - $content['name'] (so twocol gets $content['left'] and - $content['right']). - - -Creating a new Content Type: -============================ - -Content types require 1 hook and two callbacks. The hook defines what content -types are available, the first callback displays the content in a dashboard, -and the other callback does all of the administrative functions. - -hook_panels_content_types returns an array with the following information: -'callback' => The function to display the content. -'admin' => The function to administer the content. - -The callback function receives one argument: The $configuration array, as -defined by the administrative callback. - -The administrative callback recieves 3 arguments: - -$op -- the operation to perform. See below. -&$arg1 -- The first argument should be a reference and its meaning varies - based on the op. -$arg2 -- The second argument is optional, not a reference, and should - default to NULL. - -Administrative operations: - -'list': $arg1 is the configuration array. - This op is called when panels lists what content is in a content - area. It generally returns something similar to this: - return 'Views: ' . $view->name . ' (' . $view->description . ')'; - -'add button': arguments not used here. - This op is called to display the 'add content type' button; it can also - display additional information (such as the list of blocks or the - autocomplete to select a node). - - The actual button should look something like this: - $form['submit'] = array( - '#type' => 'button', - '#value' => t('Add view'), - ); - - And it's a good idea to do this, but it's not required: - - $form['#prefix'] = '
'; - $form['#suffix'] = '
'; - -'add': $arg1 == the $configuration array - This op is called to see if your add button has been clicked. It *must* - start off by checking to see if this is true: - - if ($_POST['op'] != t('Add view')) { - return; - } - - If it is true, it should process that information and return a $configuration - array populated from whatever other form items were presented in 'add button' - and whatever defaults make sense. - -'edit': $arg1 == the $configuration array - This op is called to provide an edit form for a content type. It *must* - ensure *all* information from the conf array is available, even if it - is just hidden; panels has no way to remember this data between form - clicks, so any data not put here will be lost. No buttons need to be - added to the form. - -'validate': $arg1 == $form_values, $arg2 == $form - Called to validate the 'edit' form above. - -'save': $arg1 == $form_values - Called to convert a $form_values back into a $configuration array. All - of the default types just send $form_values back as $configuration, - but if you need to do some kind of transformation, this is where it - happens. +# $Id$ + +The most current (usually -dev) version of the Panels 2 API Documentation can be +found online at http://doxy.samboyer.org/panels2/. diff --git a/INSTALL.txt b/INSTALL.txt index 6cb5273584ed91dbdd541c8f55072babd8766bd0..b9a130bb5de87438de18610c966432bd703fd1bf 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,13 +1,9 @@ -Simply activate the module. The tables you need will be installed. - -Then, go to administer >> panels - -Select 'add'. - -Choose a layout; a simple one is best if you're just exploring. - -Choose a URL for your new panel page. 'panel/test' is good if you're just exploring. - -Add some content to each of the areas. - -Click save, then click on the URL in the list to see what your new page looks like. +# $Id$ + +Place the module files in the appropriate directory. If you're not sure, +try 'sites/all/modules'. + +Then navigate to administer >> site building >> modules and activate +the panels module, along with any of the accompanying side modules you'd like to use. +The Panels module itself only provides an API - you MUST install these other modules +if you want to do much of anything. diff --git a/KNOWN_ISSUES.txt b/KNOWN_ISSUES.txt new file mode 100644 index 0000000000000000000000000000000000000000..1daab64c877c4988b835a8672deb7607c4214592 --- /dev/null +++ b/KNOWN_ISSUES.txt @@ -0,0 +1,92 @@ +# $Id$ + +Known Issue http://drupal.org/node/191771 + 'Node' panes can have two titles or have two title areas. + Cause: + Content that comes into a pane is already formatted, and this happens + in theme('node'). theme('node') assumes it will be printing a title + most of the time. However, Panels wants the titles of panes to be + consistent, so it removes the title from the node to prevent your + node.tpl.php from printing it. The result is often an empty h2 which + has odd effects. + Solution: + Add an if statement to your node.tpl.php to prevent printing that h2 + if $node->title is empty. + +Known Issue http://drupal.org/node/186454 + Internet Explorer is really bad about making the rightmost panel + fall beneath the others. + Cause: + Internet explorer calculates margins and padding differntly from + everyone else, and this makes it entirely too easy for widths + to add up to greater than the amount of allotted space, despite + using percentage widths. + Solution: + There are two solutions to this problem: + 1) In your theme, try to eliminate padding from the the
+ that directly contains your content; you can do this by + adding an empty
inside it that surrounds the content + and very specifically is set to margin: 0 and padding: 0 + + 2) if that doesn't work, override the widths of the panel-panel + divs and reduce them by 1 or 2%; usually this will give IE + enough space to quit pushing things around. + +Known Issue http://drupal.org/node/154351 + TinyMCE, FCKEditor and other wysiwyg editors really blow up on Panels + content editing. + Cause: + The modal dialogs that Panels uses are very particular about javascript + and these editors are too much for them. Also, these editors get + cranky about complicated forms with several text areas. + Solution: + Disable these editors on all of your panels admin pages. The important + URLs are admin/panels/* and panels/ajax/*. More details instructions + may follow if someone familiar with these systems submits a patch at + the above drupal.org URL. + +Known Issue http://drupal.org/node/180650 + The rounded corners style shows up as just a small graphic rather than + a full box around the panels as it shoujld. + Cause: + The rounded corners CSS relies on the ID for the panel, but the ID is + optional. + Solution: + Make sure your panel has an ID of some sort. With mini panels there is + no easy workaround as mini panels currently do not have IDs of their + own. + +Known Issue http://drupal.org/node/165745 + You see a message similar to this: + Table 'drupal.panels_info' doesn't exist query: SELECT * FROM panels_info + WHERE path = 'front_page_new' in... + + The important piece of information is 'panels_info'. + Cause: + The Meta Tags module (also known as nodewords.module) directly reads the + the panels tables and modifies its forms to add the tags. Unfortunately + for this module, Panels has changed *greatly* in the leap from 1.0 to + 2.0 and the tables aren't the same. However, the nodewords module doesn't + yet know this. Look in the nodewords issue queue for panels patches and + you should find something. + +Known Issue http://drupal.org/node/153399 + The drag and drop content UI doesn't seem to work at all under Safari. + + Cause: + Safari 2 has some serious problems with the javascript code. + Solution: + Upgrade to Safari 3 if possible. If not, use an an alternative browser + such as Firefox or Opera. + +Known Issue http://drupal.org/node/207859 + When using the secure pages module, the Panels administrative UI gives + unhelpful "An error occurred" popups when trying to add or edit content. + + Cause: + The secure pages module tries to move the entire administrative section + of the site to HTTPS, but Panels' AJAX calls are using a path that + secure pages doesn't know about. When trying to make non-secure ajax calls + from a secure page, the browser denies the call. + Solution: + The solution is to simply add panels/* to your Secure Pages configuration. \ No newline at end of file diff --git a/README.txt b/README.txt index f23574c2c38d9099c77b74cb113a7bc776430b4c..f055db8f3728d6af6927f99da366f5761d4d7758 100644 --- a/README.txt +++ b/README.txt @@ -1,22 +1,7 @@ -The panels module is the ideological son, successor to and almost complete -replacement for the dashboard module. This module allows you to create pages -that are divided into areas of the page. Where the dashboard module only gave -four areas--top, bottom, left and right--this one is a completely flexible -system that includes a couple of 2 column and 3 column layouts by default, but -is also highly extensible and other layouts can be plugged in with a little HTML -and CSS knowledge, with just enough PHP knowledge to be able to edit an include -file without breaking it. - -Perhaps most importantly, unlike the dashboard module it requires no fiddling -with PHP code to include the things you want; the interface lets you add blocks, -nodes and custom content just by selecting and clicking. - -If you want to override the CSS of a panel, the easiest way is to just copy -the CSS into your theme directory and tweak; panels will look there before -including the CSS from the module, and if it exists, will not include the -module's CSS. If you want to just change a tiny bit but keep the basic -structure, just add your changes to your style.css instead. - -If you're having problems with IE and your panels falling below your sidebars, -try setting the width of the main panel area (example, .panel-2col-stacked) to -98%. +# $Id$ + +Welcome to Panels 2. + +A little documentation should go here, but Panels 2 is a beast - you're best +off checking the online handbook on Drupal.org, or the developer/API docs, +which are available at http://doxy.samboyer.org/panels2/ diff --git a/TODO.txt b/TODO.txt index 6022dd147c22cc50bb27f9b5feb0addf148d5aab..dcfc27d1b2d60c4a85ecd423222af12d4f81779f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,12 +1,132 @@ -TODO: - -This is a brief list of the things that may still need to be done. No guarantee -of them ever getting done. - -content type: phptemplate - -access control to pages -access control to content types - -dashboard node extension - +# $Id$ + +PLEASE NOTE THAT THIS FILE CONTAINS VERY OUTDATED INFORMATION, KEPT ONLY FOR HISTORICAL PURPOSES. + +For the current plans related to Panels2, you should consult the groups.drupal.org Panels group: + +http://groups.drupal.org/panels + +Here's a rough sketch of the work plan shortly prior to the 5.x-2.0-beta4 release: + +5.x-beta3 ==> beta4 ==> beta5 ==> RC1 ==> RCs... ==> 5.x RELEASE!! ==> 5.x-2.1 + | + 6.x port ==> 6.x RCs... ==> 6.x RELEASE!! ==> 6.x-2.1 + | + 6.x-3.x-ALPHA ==> (FUTURE OF PANELS) + +========================================================== + +TODO: + +X = DONE +o = TODO +- = Skipped/postponed + +ajax/js + o remove panels_hidden, use only javascript + o Remove 'panels_set' and use the broken up drupal_get_form functions + rather than drupal_get_form for our multi-step stuff. + X Add permission checking to all the ajax functions + o Break up panels_ajax into separate entry areas [mostly done] + o ensure complete degradability + X Make sure the icons on the 'change layout' page are clickable + X Loading animation for panels-modal + X Make the titlebar outside of the overflow in the modal + o Change show/hide to use AJAX. + X Write my own drag & drop + +modules + X panels node + X mini-panel + o panels-profile + o panels-dashboard-compat (upgrade all the old dashboard installs) + +panels common + o update settings to use categories like the 'add content' popup now does. + o Clean up and better document this code + +panels page + X panels page 'default' panels + X allow panel-page to use $arg/$node/$node-type/$user substitution like Views + X allow panel-page to get a context; allow modules to provide this context. + X allow panel-page to do menu item & tab like Views. + X provide 'edit' tab for panels pages + X allow setting to not display blocks + o Clone panels page + +mini panels + X Allow mini panels to require an argument (and thus be just panel content) + +content types + - Allow creating new custom blocks. + X Get all 'block' settings into blocks. + X Remove all calls to the 'admin' callback + X Make panels_get_content_type() like using for layout + X Translate views to new content format + X Translate node to new content format + X Translate custom to new content format + X Allow content types to be aware of panel context. + X Allow content types to include or exclude themselves based upon context. + X Figure out a way for blocks to easily have per-block icons + X sort blocks in content type list + X sort views in content type list + X add weight so custom and node will float up + X make forms fit a bit better + X Allow display to have 'arguments' that can be passed on to Views + more fluidly + X give views.inc setting to NOT use arguments. + X unify title overriding. + X node_content type + X taxonomy term types + o user types + o views context sensitive conversions + +layouts + X Allow each layout to have settings + X Create a flexible layout where you can quickly create arbitrary layouts + X Fix the div div problem in all layouts + +icons + X Change configure icon + X Change remove pane icon + X Find add content icon + X Create icons for content types + +Flexible layout + o Allow fixed width sidebars + o Use a slider to control widths + +Styles: + o Convert DND to a style + X Rounded corners style + +Arguments: + X taxonomy argument + X user argument + +panels API + X Provide method to directly edit/configure an individual content block if viewer has permissions + X Provide method to control pane visibility. Make sure this is controllable from the admin so that not all users can get to it. + X Change add content to be per panel + X Make Cancel actually cancel + X Wrap calls to existing callbacks + X Make a function to simply get the IDs of all content-types + X Allow panels_edit_display to restrict available content to a preprepared list + X Use cache instead of putting everything in $_SESSION + X create panels_render_display() (or panels_display_render) + X Allow API to accept context + X Allow API to accept content + X Allow API to accept arguments + +General: + o CSS class/id name cleanup and unification. + o Make sure core blocks that require context are labelled as such. + +Documentation + o Creating new panel modules that use the API + o Creating new content types + o Creating new layouts + o Creating new styles + o Creating new arguments + o Themeing panels + o Doing interesting things with Panels diff --git a/arguments/nid.inc b/arguments/nid.inc new file mode 100644 index 0000000000000000000000000000000000000000..db9522b46ef3133a1be141d5d99729082b827d4b --- /dev/null +++ b/arguments/nid.inc @@ -0,0 +1,162 @@ + t("Node ID"), + // keyword to use for %substitution + 'keyword' => 'node', + 'description' => t('Restricts the argument to a node id.'), + 'context' => 'panels_nid_context', + 'settings form' => 'panels_nid_settings_form', + 'settings form submit' => 'panels_nid_settings_form_submit', + 'displays' => 'panels_nid_displays', + 'choose display' => 'panels_nid_choose_display', + ); + return $args; +} + +/** + * Discover if this argument gives us the node we crave. + */ +function panels_nid_context($arg = NULL, $conf = NULL, $empty = FALSE) { + // If unset it wants a generic, unfilled context. + if ($empty) { + return panels_context_create_empty('node'); + } + + if (!is_numeric($arg)) { + return FALSE; + } + + $node = node_load($arg); + if (!$node) { + return FALSE; + } + + if (array_filter($conf['types']) && empty($conf['types'][$node->type])) { + return FALSE; + } + + return panels_context_create('node', $node); +} + +/** + * Settings form for the argument + */ +function panels_nid_settings_form($conf) { + $options = array(); + foreach (node_get_types() as $type => $info) { + $options[$type] = $info->name; + } + + $form['types'] = array( + '#title' => t('Node types'), + '#type' => 'checkboxes', + '#options' => $options, + '#description' => t('You can restrict this argument to use the checked node types. Arguments from non-conforming node types will be ignored, and Panels will behave as if no argument were given. Leave all unchecked to impose no restriction.'), + '#default_value' => $conf['types'], + '#prefix' => '
', + '#suffix' => '
', + ); + + $form['own_default'] = array( + '#title' => t('Use different default display'), + '#type' => 'checkbox', + '#description' => t('If checked, when this argument is present it will use its own display rather than the default. Node types not selected in the "Own display" field will use this one.'), + '#default_value' => $conf['own_default'], + ); + + $form['displays'] = array( + '#title' => t('Own display'), + '#type' => 'checkboxes', + '#options' => $options, + '#default_value' => $conf['displays'], + '#description' => t('Each checked node type will get its own special display to layout its content. Only node types set above should be set here. Node types not set here will use the default display.'), + '#prefix' => '
', + '#suffix' => '
', + ); + + return $form; +} + +/** + * There appears to be a bit of a bug with the way we're handling forms; it causes + * 'checkboxes' to get invalid values added to them when empty. This takes care + * of that. + */ +function panels_nid_settings_form_submit(&$values) { + $types = node_get_types(); + if (!empty($values['types'])) { + foreach ($values['types'] as $type => $value) { + if (empty($types[$type])) { + unset($values['types'][$type]); + } + } + } + if (!empty($values['displays'])) { + foreach ($values['displays'] as $type => $value) { + if (empty($types[$type])) { + unset($values['displays'][$type]); + } + } + } +} + +/** + * What additional displays does this argument provide? + */ +function panels_nid_displays($conf, $id) { + $displays = array(); + if (!empty($conf['own_default'])) { + $displays['default'] = array( + 'title' => t('Node ID @id Default', array('@id' => $id)), + 'context' => 'node', + ); + } + + if (is_array($conf['displays'])) { + $options = array(); + foreach (node_get_types() as $type => $info) { + $options[$type] = $info->name; + } + foreach (array_keys(array_filter($conf['displays'])) as $type) { + $displays[$type] = array( + 'title' => t('Node ID @id @type', array('@id' => $id, '@type' => $options[$type])), + // Tell it to base the template for this display off of the default. + 'default' => 'default', + 'context' => 'node', + ); + } + } + + return $displays; +} + +/** + * Based upon the settings and the context, choose which display to use. + */ +function panels_nid_choose_display($conf, $context) { + if (empty($context->data)) { + return; + } + + if (!empty($conf['displays'][$context->data->type])) { + return $context->data->type; + } + + // Please note that 'default' is a special display. + if (!empty($conf['own_default'])) { + return 'default'; + } + + // Empty return says to use the default display. + return; +} + diff --git a/arguments/node_add.inc b/arguments/node_add.inc new file mode 100644 index 0000000000000000000000000000000000000000..ccf4fc970011904ea0be4e69be46ce367b4ef491 --- /dev/null +++ b/arguments/node_add.inc @@ -0,0 +1,154 @@ + t("Node add form"), + // keyword to use for %substitution + 'keyword' => 'node_type', + 'description' => t('Displays the node add form for a content type.'), + 'context' => 'panels_node_add_context', + 'settings form' => 'panels_node_add_settings_form', + 'settings form submit' => 'panels_node_add_settings_form_submit', + 'displays' => 'panels_node_add_displays', + 'choose display' => 'panels_node_add_choose_display', + ); + return $args; +} + +/** + * Discover if this argument gives us the node we crave. + */ +function panels_node_add_context($arg = NULL, $conf = NULL, $empty = FALSE) { + // If unset it wants a generic, unfilled context. + if (!isset($arg)) { + return panels_context_create_empty('node_add_form'); + } + + if (array_filter($conf['types']) && empty($conf['types'][$arg])) { + return FALSE; + } + + return panels_context_create('node_add_form', $arg); +} + +/** + * Settings form for the argument + */ +function panels_node_add_settings_form($conf) { + $options = array(); + foreach (node_get_types() as $type => $info) { + $options[$type] = $info->name; + } + $form['types'] = array( + '#title' => t('Node types'), + '#type' => 'checkboxes', + '#options' => $options, + '#description' => t('You can restrict this argument to use the checked node types. Arguments from non-conforming node types will be ignored, and Panels will behave as if no argument were given. Leave all unchecked to impose no restriction.'), + '#default_value' => $conf['types'], + '#prefix' => '
', + '#suffix' => '
', + ); + + $form['own_default'] = array( + '#title' => t('Use different default display'), + '#type' => 'checkbox', + '#description' => t('If checked, when this argument is present it will use its own display rather than the default. Node types not selected in the "Own display" field will use this one.'), + '#default_value' => $conf['own_default'], + ); + + $form['displays'] = array( + '#title' => t('Own display'), + '#type' => 'checkboxes', + '#options' => $options, + '#default_value' => $conf['displays'], + '#description' => t('Each checked node type will get its own special display to layout its content. Only node types set above should be set here. Node types not set here will use the default display.'), + '#prefix' => '
', + '#suffix' => '
', + ); + + return $form; +} + +/** + * There appears to be a bit of a bug with the way we're handling forms; it causes + * 'checkboxes' to get invalid values added to them when empty. This takes care + * of that. + */ +function panels_node_add_settings_form_submit(&$values) { + $types = node_get_types(); + if (!empty($values['types'])) { + foreach ($values['types'] as $type => $value) { + if (empty($types[$type])) { + unset($values['types'][$type]); + } + } + } + if (!empty($values['displays'])) { + foreach ($values['displays'] as $type => $value) { + if (empty($types[$type])) { + unset($values['displays'][$type]); + } + } + } +} + +/** + * Based upon the settings and the context, choose which display to use. + */ +function panels_node_add_choose_display($conf, $context) { + if (empty($context->form)) { + return; + } + + if (!empty($conf['displays'][$context->node_type])) { + return $context->node_type; + } + + // Please note that 'default' is a special display. + if (!empty($conf['own_default'])) { + return 'default'; + } + + // Empty return says to use the default display. + return; +} + +/** + * What additional displays does this argument provide? + */ +function panels_node_add_displays($conf, $id) { + $displays = array(); + if (!empty($conf['own_default'])) { + $displays['default'] = array( + 'title' => t('Node add form @id Default', array('@id' => $id)), + 'context' => 'node', + ); + } + + if (is_array($conf['displays'])) { + $options = array(); + foreach (node_get_types() as $type => $info) { + $options[$type] = $info->name; + } + foreach (array_keys(array_filter($conf['displays'])) as $type) { + if (!empty($options[$type])) { + $displays[$type] = array( + 'title' => t('Node add form @id @type', array('@id' => $id, '@type' => $options[$type])), + // Tell it to base the template for this display off of the default. + 'default' => 'default', + 'context' => 'node', + ); + } + } + } + + return $displays; +} + diff --git a/arguments/node_edit.inc b/arguments/node_edit.inc new file mode 100644 index 0000000000000000000000000000000000000000..480b29878aede980e9e8042c6982d3af2c8b9915 --- /dev/null +++ b/arguments/node_edit.inc @@ -0,0 +1,162 @@ + t("Node edit form"), + // keyword to use for %substitution + 'keyword' => 'node', + 'description' => t('Displays the node edit form for a node.'), + 'context' => 'panels_node_edit_context', + 'settings form' => 'panels_node_edit_settings_form', + 'settings form submit' => 'panels_node_edit_settings_form_submit', + 'displays' => 'panels_node_edit_displays', + 'choose display' => 'panels_node_edit_choose_display', + ); + return $args; +} + +/** + * Discover if this argument gives us the node we crave. + */ +function panels_node_edit_context($arg = NULL, $conf = NULL, $empty = FALSE) { + // If unset it wants a generic, unfilled context. + if ($empty) { + return panels_context_create_empty('node_edit_form'); + } + + if (!is_numeric($arg)) { + return FALSE; + } + + $node = node_load($arg); + if (!$node) { + return FALSE; + } + + if (array_filter($conf['types']) && empty($conf['types'][$node->type])) { + return FALSE; + } + + // This will perform a node_access check, so we don't have to. + return panels_context_create('node_edit_form', $node); +} + +/** + * Settings form for the argument + */ +function panels_node_edit_settings_form($conf) { + $options = array(); + foreach (node_get_types() as $type => $info) { + $options[$type] = $info->name; + } + $form['types'] = array( + '#title' => t('Node types'), + '#type' => 'checkboxes', + '#options' => $options, + '#description' => t('You can restrict this argument to use the checked node types. Arguments from non-conforming node types will be ignored, and Panels will behave as if no argument were given. Leave all unchecked to impose no restriction.'), + '#default_value' => $conf['types'], + '#prefix' => '
', + '#suffix' => '
', + ); + + $form['own_default'] = array( + '#title' => t('Use different default display'), + '#type' => 'checkbox', + '#description' => t('If checked, when this argument is present it will use its own display rather than the default. Node types not selected in the "Own display" field will use this one.'), + '#default_value' => $conf['own_default'], + ); + + $form['displays'] = array( + '#title' => t('Own display'), + '#type' => 'checkboxes', + '#options' => $options, + '#default_value' => $conf['displays'], + '#description' => t('Each checked node type will get its own special display to layout its content. Only node types set above should be set here. Node types not set here will use the default display.'), + '#prefix' => '
', + '#suffix' => '
', + ); + + return $form; +} + +/** + * There appears to be a bit of a bug with the way we're handling forms; it causes + * 'checkboxes' to get invalid values added to them when empty. This takes care + * of that. + */ +function panels_node_edit_settings_form_submit(&$values) { + $types = node_get_types(); + if (!empty($values['types'])) { + foreach ($values['types'] as $type => $value) { + if (empty($types[$type])) { + unset($values['types'][$type]); + } + } + } + if (!empty($values['displays'])) { + foreach ($values['displays'] as $type => $value) { + if (empty($types[$type])) { + unset($values['displays'][$type]); + } + } + } +} + +/** + * What additional displays does this argument provide? + */ +function panels_node_edit_displays($conf, $id) { + $displays = array(); + if (!empty($conf['own_default'])) { + $displays['default'] = array( + 'title' => t('Node edit form @id Default', array('@id' => $id)), + 'context' => 'node', + ); + } + + if (is_array($conf['displays'])) { + $options = array(); + foreach (node_get_types() as $type => $info) { + $options[$type] = $info->name; + } + foreach (array_keys(array_filter($conf['displays'])) as $type) { + $displays[$type] = array( + 'title' => t('Node edit form @id @type', array('@id' => $id, '@type' => $options[$type])), + // Tell it to base the template for this display off of the default. + 'default' => 'default', + 'context' => 'node', + ); + } + } + + return $displays; +} + +/** + * Based upon the settings and the context, choose which display to use. + */ +function panels_node_edit_choose_display($conf, $context) { + if (empty($context->form)) { + return; + } + + if (!empty($conf['displays'][$context->node_type])) { + return $context->node_type; + } + + // Please note that 'default' is a special display. + if (!empty($conf['own_default'])) { + return 'default'; + } + + // Empty return says to use the default display. + return; +} + diff --git a/arguments/term.inc b/arguments/term.inc new file mode 100644 index 0000000000000000000000000000000000000000..31a05f1adc3684d88fb5feab14140c4214f58564 --- /dev/null +++ b/arguments/term.inc @@ -0,0 +1,213 @@ + t("Taxonomy term"), + // keyword to use for %substitution + 'keyword' => 'term', + 'description' => t('Restricts the argument to a taxonomy term.'), + 'context' => 'panels_term_context', + 'settings form' => 'panels_term_settings_form', + 'settings form submit' => 'panels_term_settings_form_submit', + 'title function' => 'panels_term_title', + 'displays' => 'panels_term_displays', + 'choose display' => 'panels_term_choose_display', + ); + return $args; +} + +/** + * Discover if this argument gives us the term we crave. + */ +function panels_term_context($arg = NULL, $conf = NULL, $empty = FALSE) { + // If unset it wants a generic, unfilled context. + if ($empty) { + return panels_context_create_empty('term'); + } + + switch ($conf['input_form']) { + case 'tid': + default: + if (!is_numeric($arg)) { + return FALSE; + } + $term = taxonomy_get_term($arg); + break; + + case 'term': + $terms = taxonomy_get_term_by_name($arg); + if (count($terms) != 1) { + foreach ($terms as $potential) { + foreach ($conf['vids'] as $vid => $active) { + if ($active == 1 && $potential->vid == $vid) { + $term = $potential; + // break out of the foreaches AND the case + break 3; + } + } + } + } + $term = array_shift($terms); + break; + } + + if (empty($term)) { + return FALSE; + } + + if (!empty($conf['vids']) && array_filter($conf['vids']) && empty($conf['vids'][$term->vid])) { + return FALSE; + } + + return panels_context_create('term', $term); +} + +/** + * Settings form for the argument + */ +function panels_term_settings_form($conf) { + if (empty($conf)) { + $conf = array( + 'input_form' => 'tid', + ); + } + + $options = array(); + foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) { + $options[$vid] = $vocabulary->name; + } + + $form['input_form'] = array( + '#title' => t('Argument type'), + '#type' => 'radios', + '#options' => array('tid' => t('Term ID'), 'term' => t('Term name')), + '#default_value' => $conf['input_form'], + '#prefix' => '
', + '#suffix' => '
', + ); + + + $form['vids'] = array( + '#title' => t('Vocabularies'), + '#type' => 'checkboxes', + '#options' => $options, + '#description' => t('You can restrict this argument to use the checked vocabularies. Arguments from non-conforming vocabularies will be ignored, and Panels will behave as if no argument were given. Leave all unchecked to impose no restriction.'), + '#default_value' => $conf['vids'], + '#prefix' => '
', + '#suffix' => '
', + ); + + $form['own_default'] = array( + '#title' => t('Use different default display'), + '#type' => 'checkbox', + '#description' => t('If checked, when this argument is present it will use its own display rather than the default. Vocabularies not selected in the "Own display" field will use this one.'), + '#default_value' => $conf['own_default'], + ); + + $form['displays'] = array( + '#title' => t('Own display'), + '#type' => 'checkboxes', + '#options' => $options, + '#default_value' => $conf['displays'], + '#description' => t('Each checked vocabulary will get its own special display to layout its content. Only vocabularies set above should be set here. Vocabularies not set here will use the default display.'), + '#prefix' => '
', + '#suffix' => '
', + ); + + return $form; +} + +/** + * There appears to be a bit of a bug with the way we're handling forms; it causes + * 'checkboxes' to get invalid values added to them when empty. This takes care + * of that. + */ +function panels_term_settings_form_submit(&$values) { + $vocs = taxonomy_get_vocabularies(); + if (!empty($values['vids'])) { + foreach ($values['vids'] as $vid => $value) { + if (empty($vocs[$vid])) { + unset($values['vids'][$vid]); + } + } + } + if (!empty($values['displays'])) { + foreach ($values['displays'] as $vid => $value) { + if (empty($vocs[$vid])) { + unset($values['displays'][$vid]); + } + } + } +} + +/** + * What additional displays does this argument provide? + */ +function panels_term_displays($conf, $id) { + $displays = array(); + if (!empty($conf['own_default'])) { + $displays['default'] = array( + 'title' => t('Taxonomy term @id Default', array('@id' => $id)), + 'context' => 'term', + ); + } + + if (is_array($conf['displays'])) { + $options = array(); + foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) { + $options[$vid] = $vocabulary->name; + } + foreach (array_keys(array_filter($conf['displays'])) as $vid) { + $displays[$vid] = array( + 'title' => t('Taxonomy term @id @vocabulary', array('@id' => $id, '@vocabulary' => $options[$vid])), + // Tell it to base the template for this display off of the default. + 'default' => 'default', + 'context' => 'term', + ); + } + } + + return $displays; +} + +/** + * Based upon the settings and the context, choose which display to use. + */ +function panels_term_choose_display($conf, $context) { + if (empty($context->data)) { + return; + } + + if (!empty($conf['displays'][$context->data->vid])) { + return $context->data->vid; + } + + // Please note that 'default' is a special display. + if (!empty($conf['own_default'])) { + return 'default'; + } + + // Empty return says to use the default display. + return; +} + +/** + * Determine the title for substitution with this context + */ +function panels_term_title($context) { + if (isset($context->data->name)) { + return $context->data->name; + } + + if (isset($context->page_title)) { + return $context->page_title; + } +} + diff --git a/arguments/uid.inc b/arguments/uid.inc new file mode 100644 index 0000000000000000000000000000000000000000..7d7984bd0f1d6d56206acf654ea6e5fd74a9fa5d --- /dev/null +++ b/arguments/uid.inc @@ -0,0 +1,73 @@ + t("User ID"), + // keyword to use for %substitution + 'keyword' => 'user', + 'description' => t('Creates a user object from the argument.'), + 'context' => 'panels_uid_context', + 'settings form' => 'panels_uid_settings_form', + 'settings form submit' => 'panels_uid_settings_form_submit', + 'displays' => 'panels_uid_displays', + 'choose display' => 'panels_uid_choose_display', + ); + return $args; +} + +/** + * Discover if this argument gives us the user we crave. + */ +function panels_uid_context($arg = NULL, $conf = NULL, $empty = FALSE) { + // If unset it wants a generic, unfilled context. + if ($empty) { + return panels_context_create_empty('user'); + } + + if (!is_numeric($arg)) { + return FALSE; + } + + $user = user_load(array('uid' => $arg)); + if (!$user) { + return FALSE; + } + + return panels_context_create('user', $user); +} + +/** + * Settings form for the argument + */ +function panels_uid_settings_form($conf) { + // Doing different displays based upon role is hard because roles are not + // 1:1 like type/vocabulary are for node and term. +} + +/** + * There appears to be a bit of a bug with the way we're handling forms; it causes + * 'checkboxes' to get invalid values added to them when empty. This takes care + * of that. + */ +function panels_uid_settings_form_submit(&$values) { +} + +/** + * What additional displays does this argument provide? + */ +function panels_uid_displays($conf, $id) { + return array(); +} + +/** + * Based upon the settings and the context, choose which display to use. + */ +function panels_uid_choose_display($conf, $context) { +} diff --git a/arguments/vid.inc b/arguments/vid.inc new file mode 100644 index 0000000000000000000000000000000000000000..bcde19d7d319a982e5552186d091096dc83520f7 --- /dev/null +++ b/arguments/vid.inc @@ -0,0 +1,123 @@ + t("Vocabulary ID"), + // keyword to use for %substitution + 'keyword' => 'vocabulary', + 'description' => t('Loads a vocabulary object from the argument.'), + 'context' => 'panels_vid_context', + 'settings form' => 'panels_vid_settings_form', + 'settings form submit' => 'panels_vid_settings_form_submit', + 'displays' => 'panels_vid_displays', + 'choose display' => 'panels_vid_choose_display', + ); + return $args; +} + +/** + * Discover if this argument gives us the vocabulary we crave. + */ +function panels_vid_context($arg = NULL, $conf = NULL, $empty = FALSE) { + // If unset it wants a generic, unfilled context. + if ($empty) { + return panels_context_create_empty('vocabulary'); + } + + if (!is_numeric($arg)) { + return FALSE; + } + + $vocabulary = taxonomy_get_vocabulary($arg); + if (!$vocabulary) { + return FALSE; + } + + return panels_context_create('vocabulary', $vocabulary); +} + +/** + * Settings form for the argument + */ +function panels_vid_settings_form($conf) { + $options = array(); + foreach (taxonomy_get_vocabularies() as $vid => $voc) { + $options[$vid] = $voc->name; + } + + $form['displays'] = array( + '#title' => t('Own display'), + '#type' => 'checkboxes', + '#options' => $options, + '#default_value' => $conf['displays'], + '#description' => t('Each checked vocabulary will get its own special display to layout its content.'), + '#prefix' => '
', + '#suffix' => '
', + ); + + return $form; +} + +/** + * There appears to be a bit of a bug with the way we're handling forms; it causes + * 'checkboxes' to get invalid values added to them when empty. This takes care + * of that. + */ +function panels_vid_settings_form_submit(&$values) { + $vocs = taxonomy_get_vocabularies(); + if (!empty($values['displays'])) { + foreach ($values['displays'] as $vid => $value) { + if (empty($vocs[$vid])) { + unset($values['displays'][$vid]); + } + } + } +} + +/** + * What additional displays does this argument provide? + */ +function panels_vid_displays($conf, $id) { + $displays = array(); + + if (is_array($conf['displays'])) { + $options = array(); + foreach (taxonomy_get_vocabularies() as $vid => $info) { + $options[$vid] = $info->name; + } + foreach (array_keys(array_filter($conf['displays'])) as $vid) { + $displays[$vid] = array( + 'title' => t('vocabulary ID @id @type', array('@id' => $id, '@type' => $options[$vid])), + // Tell it to base the template for this display off of the default. + 'default' => 'default', + 'context' => 'vocabulary', + ); + } + } + + return $displays; +} + +/** + * Based upon the settings and the context, choose which display to use. + */ +function panels_vid_choose_display($conf, $context) { + if (empty($context->data)) { + return; + } + + if (!empty($conf['displays'][$context->data->vid])) { + return $context->data->vid; + } + + // Empty return says to use the default display. + return; +} + diff --git a/content_types/block.inc b/content_types/block.inc index 0d6e7f5af02a65fb1f47cab13d7967fb733b90d6..555e23522cddb46c31d0727243a506a8317bade5 100644 --- a/content_types/block.inc +++ b/content_types/block.inc @@ -1,111 +1,323 @@ - 'panels_content_block', - 'admin' => 'panels_admin_block', - ); - return $items; -} - -/** - * Output function for the 'block' content type. Outputs a block - * based on the module and delta supplied in the configuration. - */ -function panels_content_block($conf) { - $block = (object) module_invoke($conf['module'], 'block', 'view', $conf['delta']); - $block->module = $conf['module']; - $block->delta = $conf['delta']; - if ($conf['override_title']) { - $block->subject = check_plain($conf['override_title_text']); - } - - $output = theme('block', $block); - return $output; -} - -/** - * Callback to perform administrative functions on the content block - */ -function panels_admin_block($op, &$arg, $arg2 = NULL) { - switch ($op) { - case 'list': - $conf = $arg; - $block = module_invoke($conf['module'], 'block', 'list'); - $title = $block[$conf['delta']]['info']; - if ($conf['override_title']) { - $title .= ' [' . check_plain($conf['override_title_text']) . ']'; - } - return 'Block: ' . $title . ' (' . $conf['module'] . '-' . $conf['delta'] . ')'; - case 'add button': - foreach (module_list() as $module) { - $module_blocks = module_invoke($module, 'block', 'list'); - if ($module_blocks) { - $array = array(); - foreach ($module_blocks as $delta => $block) { - // strip_tags used because it goes through check_plain and that - // just looks bad. - $array["$module-$delta"] = strip_tags($block['info']); - } - $options[$module] = $array; - } - } - $form['block'] = array( - '#type' => 'select', - '#options' => $options, - '#title' => t('Block'), - ); - $form['submit'] = array( - '#type' => 'button', - '#value' => t('Add block'), - ); - - $form['#prefix'] = '
'; - $form['#suffix'] = '
'; - return $form; - case 'add': - if ($_POST['op'] != t('Add block')) { - return; - } - $form = &$arg; - $conf = array(); - list($conf['module'], $conf['delta']) = explode('-', $form['block'], 2); - // take the data given to us and return a fully formed $area object. - return $conf; - case 'edit': - $conf = &$arg; - $form['module'] = array( - '#type' => 'hidden', - '#default_value' => $conf['module'], - ); - $form['delta'] = array( - '#type' => 'hidden', - '#default_value' => $conf['delta'], - ); - $form['override_title'] = array( - '#type' => 'checkbox', - '#default_value' => $conf['override_title'], - '#title' => t('Override title'), - '#description' => t('If checked, the block title will be overridden with the override title text.') - ); - $form['override_title_text'] = array( - '#type' => 'textfield', - '#default_value' => $conf['override_title_text'], - '#title' => t('Override title text'), - '#size' => 15, - ); - - return $form; - case 'validate': - // This one has nothing to validate. - $form_values = &$arg; - $form = $arg2; - return; - case 'save': - // For this one, the form values go directly into the config. - $form = &$arg; - return $form; - } -} + t('Block'), + 'content_types' => 'panels_admin_content_types_block', + 'render callback' => 'panels_content_block', + 'add callback' => 'panels_admin_add_block', + 'edit callback' => 'panels_admin_edit_block', + 'title callback' => 'panels_admin_title_block', + 'add submit callback' => 'panels_admin_submit_block', + 'edit submit callback' => 'panels_admin_submit_block', + //'validate callback' => 'panels_admin_validate_block', + ); + return $items; +} + +/** + * Output function for the 'block' content type. Outputs a block + * based on the module and delta supplied in the configuration. + */ +function panels_content_block($conf) { + $block = (object) module_invoke($conf['module'], 'block', 'view', $conf['delta']); + if (empty($block)) { + return; + } + + $block->module = $conf['module']; + $block->delta = $conf['delta']; + + if (user_access('administer blocks')) { + $block->admin_links = array( + array( + 'title' => t('Configure block'), + 'alt' => t("Configure this pane's 'block settings' in administer >> site building >> blocks"), + 'href' => "admin/build/block/configure/$block->module/$block->delta", + 'query' => drupal_get_destination(), + ), + ); + } + + // This seems extra but it prevents an unnecessary query sometimes. + if (empty($conf['block_visibility']) && $block->module != 'block') { + return $block; + } + + // Test for block visibility + $result = db_query("SELECT title, pages, visibility FROM {blocks} WHERE module = '%s' AND delta = '%s'", $block->module, $block->delta); + $block_visibility = db_fetch_object($result); + + if ($block->module == 'block') { + $block->title = $block_visibility->title; + } + + if (empty($conf['block_visibility'])) { + return $block; + } + + if ($block_visibility && $block_visibility->pages) { + if ($block_visibility->visibility < 2) { + $path = drupal_get_path_alias($_GET['q']); + $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\($|\|)/'), array('|', '.*', '\1'. preg_quote(variable_get('site_frontpage', 'node'), '/') .'\2'), preg_quote($block_visibility->pages, '/')) .')$/'; + $page_match = !($block_visibility->visibility xor preg_match($regexp, $path)); + } + else { + $page_match = drupal_eval($block_visibility->pages); + } + } + else { + $page_match = TRUE; + } + + if ($page_match) { + return $block; + } +} + +/** + * Return all block content types available. + * + * @ingroup hook_invocations + * + * Modules wanting to make special adjustments the way that panels handles their blocks + * can implement an extension to the hook_block() family, where the function name is + * of the form "$module . '_panels_block_info'". + */ +function panels_admin_content_types_block() { + $types = array(); + foreach (module_list() as $module) { + $module_blocks = module_invoke($module, 'block', 'list'); + if ($module_blocks) { + foreach ($module_blocks as $delta => $block) { + // strip_tags used because it goes through check_plain and that + // just looks bad. + $info = array( + 'title' => strip_tags($block['info']), + ); + + // Ask around for further information by invoking the hook_block() extension. + $function = $module . '_panels_block_info'; + if (!function_exists($function)) { + $function = 'panels_default_block_info'; + } + $function($module, $delta, $info); + + // this check means modules can remove their blocks; particularly useful + // if they offer the block some other way (like we do for views) + if ($info) { + $types["$module-$delta"] = $info; + } + } + } + } + return $types; +} + +/** + * Returns the form for a new block. + */ +function panels_admin_add_block($id, $parents, $conf = array()) { + list($conf['module'], $conf['delta']) = explode('-', $id, 2); + return panels_admin_edit_block($id, $parents, $conf); +} + +/** + * Returns an edit form for a block. + */ +function panels_admin_edit_block($id, $parents, $conf) { + $form['module'] = array( + '#type' => 'value', + '#value' => $conf['module'], + ); + $form['delta'] = array( + '#type' => 'value', + '#value' => $conf['delta'], + ); + + if (user_access('administer advanced pane settings')) { + $form['block_visibility'] = array( + '#type' => 'checkbox', + '#title' => t('Use block visibility settings (see block config)'), + '#default_value' => !empty($conf['block_visibility']), + '#description' => t('If checked, the block visibility settings for this block will apply to this block.'), + ); + // Module-specific block configurations. + if ($settings = module_invoke($conf['module'], 'block', 'configure', $conf['delta'])) { + // Specifically modify a couple of core block forms. + if ($conf['module'] == 'block') { + unset($settings['submit']); + $settings['info']['#type'] = 'value'; + $settings['info']['#value'] = $settings['info']['#default_value']; + } + panels_admin_fix_block_tree($settings); + $form['block_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Block settings'), + '#description' => t('Settings in this section are global and are for all blocks of this type, anywhere in the system.'), + '#tree' => FALSE, + ); + + + $form['block_settings'] += $settings; + } + } + + return $form; +} + +function panels_admin_submit_block(&$form_values) { + if (!empty($form_values['block_settings'])) { + module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values['block_settings']); + } +} + +/** + * Because form api cannot collapse just part of a tree, and the block settings + * assume no tree, we have to collapse the tree ourselves. + */ +function panels_admin_fix_block_tree(&$form, $key = NULL) { + if ($key) { + if (!empty($form['#parents'])) { + $form['#parents'] = array_merge(array('configuration', 'block_settings'), $form['#parents']); + } + else if (empty($form['#tree'])) { + $form['#parents'] = array('configuration', 'block_settings', $key); + } + } + + if (isset($form['#type']) && $form['#type'] == 'textarea' && !empty($form['#rows']) && $form['#rows'] > 10) { + $form['#rows'] = 10; + } + + foreach (element_children($form) as $key) { + panels_admin_fix_block_tree($form[$key], $key); + } +} + +/** + * Returns the administrative title for a type. + */ +function panels_admin_title_block($conf) { + $block = module_invoke($conf['module'], 'block', 'list'); + if (empty($block) || empty($block[$conf['delta']])) { + return t('Deleted/missing block @module-@delta', array('@module' => $conf['module'], '@delta' => $conf['delta'])); + } + + $title = filter_xss_admin($block[$conf['delta']]['info']); + return $title; +} + +function panels_default_block_info($module, $delta, &$info) { + $core_modules = array('aggregator', 'block', 'blog', 'blogapi', 'book', 'color', 'comment', 'contact', 'drupal', 'filter', 'forum', 'help', 'legacy', 'locale', 'menu', 'node', 'path', 'ping', 'poll', 'profile', 'search', 'statistics', 'taxonomy', 'throttle', 'tracker', 'upload', 'user', 'watchdog', 'system'); + + if (in_array($module, $core_modules)) { + $info['icon'] = 'icon_core_block.png'; + $info['category'] = array(t('Core blocks'), -5); + } + else { + $info['icon'] = 'icon_contrib_block.png'; + $info['category'] = t('Contributed blocks'); + } +} + +// These are all on behalf of modules that don't implement panels but we that +// we care about. +function menu_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_block_menu.png'; + $info['category'] = array(t('Menus'), -2); +} + +function forum_panels_block_info($module, $delta, &$info) { + $info['category'] = t('Core blocks'); + switch ($delta) { + case '0': + $info['icon'] = 'icon_core_activeforumtopics.png'; + break; + + case '1': + $info['icon'] = 'icon_core_newforumtopics.png'; + break; + + default: + // safety net + panels_default_block_info($module, $delta, $info); + } +} + +function profile_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_authorinformation.png'; + $info['category'] = t('Core blocks'); +} + +function book_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_booknavigation.png'; + $info['category'] = t('Core blocks'); +} + +function blog_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_recentblogposts.png'; + $info['category'] = t('Core blocks'); +} + +function poll_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_recentpoll.png'; + $info['category'] = t('Core blocks'); +} + +function comment_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_recentcomments.png'; + $info['category'] = t('Core blocks'); +} + +function search_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_searchform.png'; + $info['category'] = t('Core blocks'); +} + +function node_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_syndicate.png'; + $info['category'] = t('Core blocks'); +} + +function aggregator_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_syndicate.png'; + $info['category'] = array(t('Syndicated Feeds'), -3); +} + +function block_panels_block_info($module, $delta, &$info) { + $info['icon'] = 'icon_core_block_empty.png'; + $info['category'] = array(t('Custom'), -10); +} + +function user_panels_block_info($module, $delta, &$info) { + $info['category'] = t('Core blocks'); + switch ($delta) { + case '0': + $info['icon'] = 'icon_core_userlogin.png'; + break; + + case '1': + $info['icon'] = 'icon_core_navigation.png'; + $info['category'] = array(t('Menus'), -2); + break; + + case '2': + $info['icon'] = 'icon_core_whosnew.png'; + break; + + case '2': + $info['icon'] = 'icon_core_whosonline.png'; + break; + + default: + // safety net + panels_default_block_info($module, $delta, $info); + } +} + diff --git a/content_types/custom.inc b/content_types/custom.inc index 37d863afc882408a3d615cffaeb08fcfad6a8f33..70f1006e1471bd0c61164c9de21d1e0ddc34cfb0 100644 --- a/content_types/custom.inc +++ b/content_types/custom.inc @@ -1,90 +1,114 @@ - 'panels_content_custom', - 'admin' => 'panels_admin_custom', - ); - return $items; -} - -/** - * Output function for the 'custom' content type. Outputs a custom - * based on the module and delta supplied in the configuration. - */ -function panels_content_custom($conf) { - $title = filter_xss_admin($conf['title']); - $body = check_markup($conf['body'], $conf['format'], FALSE); - return theme('panels_content_custom', $title, $body); -} - -function theme_panels_content_custom($title, $body) { - $output = '
'; - if ($title) { - $output .= '

' . $title . '

'; - } - $output .= $body; - $output .= '
'; -EOT; - return $output; -} -/** - * Callback to perform administrative functions on the content block - */ -function panels_admin_custom($op, &$arg, $arg2 = NULL) { - switch ($op) { - case 'list': - $conf = $arg; - return 'Custom: ' . filter_xss_admin($conf['title']); - case 'add button': - $form['title'] = array( - '#title' => t('Enter an optional title for custom content you define'), - '#type' => 'textfield', - '#maxlength' => 512, - '#weight' => -10, - ); - $form['submit'] = array( - '#type' => 'button', - '#value' => t('Add custom'), - ); - - $form['#prefix'] = '
'; - $form['#suffix'] = '
'; - return $form; - case 'add': - if ($_POST['op'] != t('Add custom')) { - return; - } - return $arg; - case 'edit': - $conf = &$arg; - $form['title'] = array( - '#type' => 'textfield', - '#default_value' => $conf['title'], - '#title' => t('Title'), - '#description' => t('Title'), - '#size' => 15, - ); - $form['body'] = array( - '#title' => t('Body'), - '#type' => 'textarea', - '#default_value' => $conf['body'], - '#rows' => 10, - '#cols' => 20, - ); - $arg2[] = 'format'; - $form['format'] = filter_form($conf['format'], 1, $arg2); - - return $form; - case 'validate': - // This one has nothing to validate. - $form = &$arg; - return; - case 'save': - // For this one, the form values go directly into the config. - $form = &$arg; - return $form; - } -} + t('Custom'), + 'weight' => -10, + 'single' => TRUE, + 'content_types' => 'panels_admin_content_types_custom', + 'render callback' => 'panels_content_custom', + 'editor render callback' => 'panels_admin_content_custom', + 'add callback' => 'panels_admin_edit_custom', + 'edit callback' => 'panels_admin_edit_custom', + 'title callback' => 'panels_admin_title_custom', + 'no override title' => TRUE, + ); + return $items; +} + +/** + * Output function for the 'custom' content type. Outputs a custom + * based on the module and delta supplied in the configuration. + */ +function panels_content_custom($conf) { + static $delta = 0; + + $block = new stdClass(); + $block->module = 'custom'; + $block->delta = ++$delta; + $block->subject = filter_xss_admin($conf['title']); + $block->content = check_markup($conf['body'], $conf['format'], FALSE); + + return $block; +} + +/** + * Render callback for when the custom content is in the editor so that people + * can have a preview on the spot. + * + * @param panels_display $display + * @param stdClass $pane + * @return stdClass $block + */ +function panels_admin_content_custom($display, $pane) { + $block = new stdClass(); + $block->title = filter_xss_admin($pane->configuration['title']); + // We don't want to render php output on preview here, because if something is + // wrong the whole display will be borked. So we check to see if the php + // evaluator filter is being used, and make a temporary change to the filter + // so that we get the printed php, not the eval'ed php. + $php_filter = FALSE; + foreach (filter_list_format($pane->configuration['format']) as $filter) { + if ($filter->name == 'PHP evaluator') { // TODO stupid way to check + $php_filter = TRUE; + } + } + // If a php filter is active, pass 1 to use core's most restrictive filter. + $block->content = check_markup($pane->configuration['body'], $php_filter ? 1 : $pane->configuration['format']); + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_custom() { + return array( + 'custom' => array( + 'title' => t('New custom content'), + 'icon' => 'icon_block_custom.png', + 'path' => panels_get_path('content_types/custom'), + 'description' => t('Create a completely custom piece of HTML content.'), + 'category' => array(t('Custom'), -10), + ), + ); +} + +/** + * Returns an edit form for the custom type. + */ +function panels_admin_edit_custom($id, $parents, $conf = NULL) { + if (empty($conf)) { + $conf = array('title' => '', 'body' => '', 'format' => FILTER_FORMAT_DEFAULT); + } + $form['title'] = array( + '#type' => 'textfield', + '#default_value' => $conf['title'], + '#title' => t('Title'), + ); + $form['body'] = array( + '#title' => t('Body'), + '#type' => 'textarea', + '#default_value' => $conf['body'], + ); + $parents[] = 'format'; + $form['format'] = filter_form($conf['format'], 1, $parents); + + return $form; +} + +/** + * Returns the administrative title for a type. + */ +function panels_admin_title_custom($conf) { + $output = t('Custom'); + if (!empty($conf['title'])) { + $output .= " (" . filter_xss_admin($conf['title']) . ")"; + } + return $output; +} + diff --git a/content_types/custom_php.inc b/content_types/custom_php.inc new file mode 100644 index 0000000000000000000000000000000000000000..f667289a62c7fa4b3f3dfbb112d1449bda7627d5 --- /dev/null +++ b/content_types/custom_php.inc @@ -0,0 +1,125 @@ + t('Custom PHP'), + 'weight' => -10, + 'single' => TRUE, + 'content_types' => 'panels_admin_content_types_custom_php', + 'render callback' => 'panels_content_custom_php', + 'editor render callback' => 'panels_admin_content_custom_php', + 'add callback' => 'panels_admin_edit_custom_php', + 'edit callback' => 'panels_admin_edit_custom_php', + 'title callback' => 'panels_admin_title_custom_php', + 'no override title' => TRUE, + ); + return $items; +} + +/** + * Output function for the 'custom_php' content type. Allows sufficiently + * privileged users to enter custom php code that is evaluated while + * $context is in scope. Use with caution! + */ +function panels_content_custom_php($conf, $panel_args, $context) { + static $delta = 0; + $block = new stdClass(); + + // eval() the php that has been entered by the user. + eval($conf['body']); + $block->delta = ++$delta; + // Put our default member values into a separate array. + $members = array( + 'module' => 'custom', + 'subject' => filter_xss_admin($conf['title']), + 'content' => '', + ); + // Iterate through the array of defaults and assign put in + // any values that haven't already been set by the custom + // php code. + foreach ($members as $member => $value) { + if (empty($block->$member)) { + $block->$member = $value; + } + } + + return $block; +} + +/** + * Render callback for when the custom content is in the editor so that people + * can have a preview on the spot. + * + * @param panels_display $display + * @param stdClass $pane + * @return stdClass $block + */ +function panels_admin_content_custom_php($display, $pane) { + $block = new stdClass(); + $block->title = filter_xss_admin($pane->configuration['title']); + // We don't want to render php output on preview here, because if something is + // wrong the whole display will be borked. + $block->content = check_markup($pane->configuration['body'], 1); + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_custom_php() { + if (filter_access(2)) { + return array( + 'custom_php' => array( + 'title' => t('Custom PHP content'), + 'icon' => 'icon_block_custom.png', + 'path' => panels_get_path('content_types/custom'), + 'description' => t('Custom PHP block with access to Panels context data. Use sparingly, and with caution!'), + 'category' => array(t('Custom'), -10), + ), + ); + } +} + +/** + * Returns an edit form for the custom_php type. + */ +function panels_admin_edit_custom_php($id, $parents, $conf = NULL) { + if (!is_array($conf)) { + $conf = array('title' => '', 'body' => ''); + } + $form['title'] = array( + '#type' => 'textfield', + '#default_value' => $conf['title'], + '#title' => t('Title'), + ); + $body = t('Any content you want to have passed along to theme function for displaying MUST be stored in $block->content.') . "\n\n"; + $body .= t('MAKE SURE YOU ERASE ALL OF THIS HELP TEXT. Only error-free php code should be saved in this field.') . "\n\n"; + $body .= t('Do NOT use php tags.'); + + $form['body'] = array( + '#title' => t('Body'), + '#type' => 'textarea', + '#default_value' => isset($conf['body']) ? $conf['body'] : $body, + '#rows' => 10, + '#description' => t('The PHP code you enter here will be evaluated at render-time. Any context data present in the display will be available to this code in the $context variable. DO NOT include @php tags.', array('@php' => '')), + ); + + return $form; +} + +/** + * Returns the administrative title for a type. + */ +function panels_admin_title_custom_php($conf) { + $output = t('Custom'); + if ($conf['title']) { + $output .= " (" . filter_xss_admin($conf['title']) . ")"; + } + return $output; +} + diff --git a/content_types/form.inc b/content_types/form.inc new file mode 100644 index 0000000000000000000000000000000000000000..74f5f4831f65f04421d8d1cedc8e530b18ac6b24 --- /dev/null +++ b/content_types/form.inc @@ -0,0 +1,72 @@ + t('Generic form'), + 'content_types' => 'panels_admin_content_types_form', + // only provides a single content type + 'single' => TRUE, + 'render callback' => 'panels_content_form', + 'add callback' => 'panels_admin_edit_form', + 'edit callback' => 'panels_admin_edit_form', + 'title callback' => 'panels_admin_title_form', + 'render last' => TRUE, + ); + return $items; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_form() { + return array( + 'node_type' => array( + 'title' => t('General form'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('The general parts of a form.'), + 'required context' => new panels_required_context(t('Form'), 'form'), + 'category' => array(t('Form'), -9), + ), + ); +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_form($conf, $panel_args, &$context) { + $block = new stdClass(); + $block->module = 'form'; + + if (isset($context->form)) { + $block->subject = $context->form_title; + if (!empty($context->form_id)) { + // If this is a form, drupal_render it. + $block->content = drupal_render($context->form); + } + else { + // Otherwise just spit back what we were given. This is probably an + // error message or something. + $block->content = $context->form; + } + $block->delta = $context->form_id; + } + else { + $block->subject = t('Form'); + $block->content = t('Form goes here.'); + $block->delta = 'unknown'; + } + + return $block; +} + +function panels_admin_title_form($conf, $context) { + return t('"@s" base form', array('@s' => $context->identifier)); +} + diff --git a/content_types/node.inc b/content_types/node.inc index 1383ab7ac9ca39d33d0329951e9e7a33e6431215..90780914ed6a329bd43b78bd7180b575e3cbd2e9 100644 --- a/content_types/node.inc +++ b/content_types/node.inc @@ -1,96 +1,5 @@ - 'panels_content_node', - 'admin' => 'panels_admin_node', - ); - return $items; -} - -/** - * Output function for the 'node' content type. Outputs a node - * based on the module and delta supplied in the configuration. - */ -function panels_content_node($conf) { - $node = node_load($conf['nid']); - $output = node_view($node, $conf['teaser'], FALSE, $conf['links']); - return $output; -} - -/** - * Callback to perform administrative functions on the content block - */ -function panels_admin_node($op, &$arg, $arg2 = NULL) { - switch ($op) { - case 'list': - $conf = $arg; - $node = node_load($conf['nid']); - return 'Node: ' . check_plain($node->title); - case 'add button': - $form['nid'] = array( - '#title' => t('Enter the title or NID of a post'), - '#type' => 'textfield', - '#maxlength' => 512, - '#autocomplete_path' => 'panels/node/autocomplete', - '#weight' => -10, - ); - $form['submit'] = array( - '#type' => 'button', - '#value' => t('Add post'), - ); - - $form['#prefix'] = '
'; - $form['#suffix'] = '
'; - return $form; - case 'add': - if ($_POST['op'] != t('Add post')) { - return; - } - $form = &$arg; - if (is_numeric($form['nid'])) { - $conf = array(); - $conf['nid'] = $form['nid']; - } - else { - $conf = db_fetch_array(db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE LOWER(title) = LOWER('%s')"), $form['nid'])); - if (!$conf['nid']) { - drupal_set_message(t('Unable to find "%s"', array('%s' => check_plain($form['nid'])))); - return; - } - } - // default to just teaser - $conf['teaser'] = TRUE; - return $conf; - case 'edit': - $conf = &$arg; - $form['nid'] = array( - '#type' => 'hidden', - '#default_value' => $conf['nid'], - ); - $form['teaser'] = array( - '#title' => t('Teaser'), - '#type' => 'checkbox', - '#default_value' => $conf['teaser'], - '#description' => t('Check here to show only the node teaser'), - ); - $form['links'] = array( - '#type' => 'checkbox', - '#default_value' => $conf['links'], - '#title' => t('Display links'), - '#description' => t('Check here to display the links with the post.') - ); - - return $form; - case 'validate': - // This one has nothing to validate. - $form = &$arg; - return; - case 'save': - // For this one, the form values go directly into the config. - $form = &$arg; - return $form; - } -} + t('Node type description'), + // only provides a single content type + 'single' => TRUE, + 'content_types' => 'panels_admin_content_types_node_attachments', + 'render callback' => 'panels_content_node_attachments', +// 'add callback' => 'panels_admin_edit_node_attachments', +// 'edit callback' => 'panels_admin_edit_node_attachments', + 'title callback' => 'panels_admin_title_node_attachments', + ); + return $items; +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_node_attachments($conf, $panel_args, $context) { + $node = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'attachments'; + + $block->subject = t('Attached files'); + if ($node) { + $block->content = theme('upload_attachments', $node->files); + $block->delta = $node->nid; + } + else { + $block->content = t('Attached files go here.'); + $block->delta = 'unknown'; + } + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_node_attachments() { + return array( + 'node_type' => array( + 'title' => t('Attached files'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('A list of files attached to the node.'), + 'required context' => new panels_required_context(t('Node'), 'node'), + 'category' => array(t('Node context'), -9), + ), + ); +} + +function panels_admin_title_node_attachments($conf, $context) { + return t('"@s" attachments', array('@s' => $context->identifier)); +} + diff --git a/content_types/node_book_nav.inc b/content_types/node_book_nav.inc new file mode 100644 index 0000000000000000000000000000000000000000..669c05175c2503c1f1e26b7b13a6af7cc6b7781e --- /dev/null +++ b/content_types/node_book_nav.inc @@ -0,0 +1,63 @@ + t('Node book navigation'), + // only provides a single content type + 'single' => TRUE, + 'content_types' => 'panels_admin_content_types_node_book_nav', + 'render callback' => 'panels_content_node_book_nav', + 'title callback' => 'panels_admin_title_node_book_nav', + ); + return $items; + } +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_node_book_nav($conf, $panel_args, $context) { + $node = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'book_nav'; + + $block->subject = t('Book navigation'); + if ($node) { + $block->content = theme('book_navigation', $node); + $block->delta = $node->nid; + } + else { + $block->content = t('Book navigation goes here.'); + $block->delta = 'unknown'; + } + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_node_book_nav() { + return array( + 'node_type' => array( + 'title' => t('Book navigation'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('A list of files attached to the node.'), + 'required context' => new panels_required_context(t('Node'), 'node'), + 'category' => array(t('Node context'), -9), + ), + ); +} + +function panels_admin_title_node_book_nav($conf, $context) { + return t('"@s" book navigation', array('@s' => $context->identifier)); +} + diff --git a/content_types/node_comment_form.inc b/content_types/node_comment_form.inc new file mode 100644 index 0000000000000000000000000000000000000000..aa9fa10fc67a7263f8c9b3e56d4c968dba0a2dd3 --- /dev/null +++ b/content_types/node_comment_form.inc @@ -0,0 +1,79 @@ + t('Node comment form'), + // only provides a single content type + 'single' => TRUE, + 'content_types' => 'panels_admin_content_types_node_comment_form', + 'render callback' => 'panels_content_node_comment_form', + 'add callback' => 'panels_admin_edit_node_comment_form', + 'edit callback' => 'panels_admin_edit_node_comment_form', + 'title callback' => 'panels_admin_title_node_comment_form', + ); + return $items; + } +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_node_comment_form($conf, $panel_args, $context) { + $node = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'comments'; + $block->delta = $node->nid; + + $block->subject = t('Add comment'); + + if (empty($node)) { + $block->content = t('Comment form here.'); + } + else { + if (user_access('post comments') && node_comment_mode($node->nid) == COMMENT_NODE_READ_WRITE) { + $block->content = drupal_get_form('panels_comment_form', array('nid' => $node->nid)); + } + } + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_node_comment_form() { + return array( + 'comment_form' => array( + 'title' => t('Comment form'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('A form to add a new comment.'), + 'required context' => new panels_required_context(t('Node'), 'node'), + 'category' => array(t('Node context'), -9), + ), + ); +} + +function panels_admin_title_node_comment_form($conf, $context) { + return t('"@s" comment form', array('@s' => $context->identifier)); +} + +function panels_comment_form($edit) { + $form = comment_form($edit); + // force the form to come back to here. + $url = parse_url($_GET['q']); + $path = $url['path']; + $form['#action'] = url($path, NULL, 'new'); + $form['#redirect'] = array($path, NULL, 'new'); + $form['#validate']['comment_form_validate'] = array(); + $form['#submit']['comment_form_submit'] = array(); + return $form; +} + diff --git a/content_types/node_comments.inc b/content_types/node_comments.inc new file mode 100644 index 0000000000000000000000000000000000000000..614f24f5cadffc366b3d95f7dc3933dd6606a1b1 --- /dev/null +++ b/content_types/node_comments.inc @@ -0,0 +1,190 @@ + t('Node comments'), + 'content_types' => 'panels_admin_content_types_node_comments', + 'single' => TRUE, + 'render callback' => 'panels_content_node_comments', + 'add callback' => 'panels_admin_edit_node_comments', + 'edit callback' => 'panels_admin_edit_node_comments', + 'title callback' => 'panels_admin_title_node_comments', + ); + } + return $items; +} + +/** + * Output function for the 'node comments' content type. Outputs the comments + * that have been made on a given nid, as provided by the $context. + */ +function panels_content_node_comments($conf, $panel_args, $context) { + $node = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'comments'; + $block->delta = $node->nid; + + $block->subject = t('Comments'); + if (empty($node)) { + $block->content = t('Node comments go here.'); + } + else { + $block->content = panels_comment_render($node, $conf); + // Update the history table, stating that this user viewed this node. + node_tag_new($node->nid); + } + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_node_comments() { + return array( + 'comments' => array( + 'title' => t('Node comments'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('The comments of the referenced node.'), + 'required context' => new panels_required_context(t('Node'), 'node'), + 'category' => array(t('Node context'), -9), + ), + ); +} + +function panels_admin_edit_node_comments($id, $parents, $conf = array()) { + if (empty($conf)) { + $conf = array( + 'mode' => _comment_get_display_setting('mode'), + 'order' => _comment_get_display_setting('sort'), + 'comments_per_page' => _comment_get_display_setting('comments_per_page'), + ); + } + + $form['mode'] = array( + '#type' => 'select', + '#title' => t('Mode'), + '#default_value' => $conf['mode'], + '#options' => _comment_get_modes(), + '#weight' => 1, + ); + $form['order'] = array( + '#type' => 'select', + '#title' => t('Sort'), + '#default_value' => $conf['order'], + '#options' => _comment_get_orders(), + '#weight' => 2, + ); + foreach (_comment_per_page() as $i) { + $options[$i] = t('!a comments per page', array('!a' => $i)); + } + $form['comments_per_page'] = array('#type' => 'select', + '#title' => t('Pager'), + '#default_value' => $conf['comments_per_page'], + '#options' => $options, + '#weight' => 3, + ); + return $form; +} + +function panels_admin_title_node_comments($conf, $context) { + return t('"@s" comments', array('@s' => $context->identifier)); +} + +/** + * This function is a somewhat stripped down version of comment_render + * that removes a bunch of cruft that we both don't need, and makes it + * difficult to modify this. + */ +function panels_comment_render($node, $conf) { + if (!user_access('access comments')) { + return; + } + + $mode = $conf['mode']; + $order = $conf['order']; + $comments_per_page = $conf['comments_per_page']; + + // Multiple comment view + $query_count = 'SELECT COUNT(*) FROM {comments} WHERE nid = %d'; + $query = 'SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.picture, u.data, c.score, c.users, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d'; + + $query_args = array($node->nid); + if (!user_access('administer comments')) { + $query .= ' AND c.status = %d'; + $query_count .= ' AND status = %d'; + $query_args[] = COMMENT_PUBLISHED; + } + + if ($order == COMMENT_ORDER_NEWEST_FIRST) { + if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) { + $query .= ' ORDER BY c.timestamp DESC'; + } + else { + $query .= ' ORDER BY c.thread DESC'; + } + } + else if ($order == COMMENT_ORDER_OLDEST_FIRST) { + if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) { + $query .= ' ORDER BY c.timestamp'; + } + else { + $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))'; + } + } + + // Start a form, for use with comment control. + $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args); + + $divs = 0; + $last_depth = 0; + drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css'); + while ($comment = db_fetch_object($result)) { + $comment = drupal_unpack($comment); + $comment->name = $comment->uid ? $comment->registered_name : $comment->name; + $comment->depth = count(explode('.', $comment->thread)) - 1; + + if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) { + if ($comment->depth > $last_depth) { + $divs++; + $output .= '
'; + $last_depth++; + } + else { + while ($comment->depth < $last_depth) { + $divs--; + $output .= '
'; + $last_depth--; + } + } + } + + if ($mode == COMMENT_MODE_FLAT_COLLAPSED) { + $output .= theme('comment_flat_collapsed', $comment); + } + else if ($mode == COMMENT_MODE_FLAT_EXPANDED) { + $output .= theme('comment_flat_expanded', $comment); + } + else if ($mode == COMMENT_MODE_THREADED_COLLAPSED) { + $output .= theme('comment_thread_collapsed', $comment); + } + else if ($mode == COMMENT_MODE_THREADED_EXPANDED) { + $output .= theme('comment_thread_expanded', $comment); + } + } + for ($i = 0; $i < $divs; $i++) { + $output .= '
'; + } + $output .= theme('pager', NULL, $comments_per_page, 0); + + return $output; +} + diff --git a/content_types/node_content.inc b/content_types/node_content.inc new file mode 100644 index 0000000000000000000000000000000000000000..a9351d2b9ff621e459c14d1733fff7fc4c886d7f --- /dev/null +++ b/content_types/node_content.inc @@ -0,0 +1,205 @@ + t('Node content'), + 'weight' => -10, + // only provides a single content type + 'single' => TRUE, + 'content_types' => 'panels_admin_content_types_node_content', + 'render callback' => 'panels_content_node_content', + 'add callback' => 'panels_admin_edit_node_content', + 'edit callback' => 'panels_admin_edit_node_content', + 'title callback' => 'panels_admin_title_node_content', + ); + return $items; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_node_content() { + return array( + 'content' => array( + 'title' => t('Node content'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('The content of the referenced node.'), + 'required context' => new panels_required_context(t('Node'), 'node'), + 'category' => array(t('Node context'), -9), + ), + ); +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_node_content($conf, $panel_args, $context) { + if (!empty($context) && empty($context->data)) { + return; + } + + $node = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'node'; + $block->delta = $node->nid; + + if (empty($node)) { + $block->delta = 'placeholder'; + $block->subject = t('Node title.'); + $block->content = t('Node content goes here.'); + } + else { + if (!empty($conf['identifier'])) { + $node->panel_identifier = $conf['identifier']; + } + + $block->subject = $node->title; + + unset($node->title); + $block->content = panels_admin_node_content($node, $conf); + } + + if (node_access('update', $node)) { + $block->admin_links['update'] = array( + 'title' => t('Edit node'), + 'alt' => t("Edit this node"), + 'href' => "node/$node->nid/edit", + 'query' => drupal_get_destination(), + ); + } + + if (!empty($conf['link']) && $node) { + $block->title_link = "node/$node->nid"; + } + + return $block; +} + +function panels_admin_node_content($node, $conf) { + // Remove the delimiter (if any) that separates the teaser from the body. + $node->body = str_replace('', '', $node->body); + + // The 'view' hook can be implemented to overwrite the default function + // to display nodes. + if (node_hook($node, 'view')) { + $node = node_invoke($node, 'view', $conf['teaser'], $conf['page']); + } + else { + $node = node_prepare($node, $conf['teaser']); + } + + if (empty($conf['no_extras'])) { + // Allow modules to make their own additions to the node. + node_invoke_nodeapi($node, 'view', $conf['teaser'], $conf['page']); + } + + if ($conf['links']) { + $node->links = module_invoke_all('link', 'node', $node, $conf['teaser']); + + foreach (module_implements('link_alter') AS $module) { + $function = $module .'_link_alter'; + $function($node, $node->links); + } + } + + // Set the proper node part, then unset unused $node part so that a bad + // theme can not open a security hole. + $content = drupal_render($node->content); + if ($conf['teaser']) { + $node->teaser = $content; + unset($node->body); + } + else { + $node->body = $content; + unset($node->teaser); + } + + // Allow modules to modify the fully-built node. + node_invoke_nodeapi($node, 'alter', $conf['teaser'], $conf['page']); + + return theme('node', $node, $conf['teaser'], $conf['page']); +} + +/** + * Returns an edit form for the custom type. + */ +function panels_admin_edit_node_content($id, $parents, $conf = array()) { + if ($conf == array()) { + $conf = array( + 'links' => TRUE, + 'page' => TRUE, + 'no_extras' => TRUE, + ); + } + + $form['aligner_start'] = array( + '#value' => '
', + ); + $form['override_title'] = array( + '#type' => 'checkbox', + '#default_value' => $conf['override_title'], + '#title' => t('Override title'), + '#id' => 'override-title-checkbox', + ); + $form['override_title_text'] = array( + '#type' => 'textfield', + '#default_value' => $conf['override_title_text'], + '#size' => 35, + '#id' => 'override-title-textfield', + ); + $form['aligner_stop'] = array( + '#value' => '
', + ); + $form['link'] = array( + '#title' => t('Link title to node'), + '#type' => 'checkbox', + '#default_value' => $conf['link'], + '#description' => t('Check here to make the title link to the node.'), + ); + $form['teaser'] = array( + '#title' => t('Teaser'), + '#type' => 'checkbox', + '#default_value' => $conf['teaser'], + '#description' => t('Check here to show only the node teaser.'), + ); + $form['page'] = array( + '#type' => 'checkbox', + '#default_value' => $conf['page'], + '#title' => t('Node page'), + '#description' => t('Check here if the node is being displayed on a page by itself.'), + ); + $form['links'] = array( + '#type' => 'checkbox', + '#default_value' => $conf['links'], + '#title' => t('Display links'), + '#description' => t('Check here to display the links with the post.'), + ); + + $form['no_extras'] = array( + '#type' => 'checkbox', + '#default_value' => $conf['no_extras'], + '#title' => t('No extras'), + '#description' => t('Check here to disable additions that modules might make to the node, such as file attachments and CCK fields; this should just display the basic teaser or body.'), + ); + + $form['identifier'] = array( + '#type' => 'textfield', + '#default_value' => $conf['identifier'], + '#title' => t('Identifier'), + '#description' => t('Whatever is placed here will appear in $node->panel_identifier to make it easier to theme a node or part of a node as necessary.'), + ); + + return $form; +} + +function panels_admin_title_node_content($conf, $context) { + return t('"@s" content', array('@s' => $context->identifier)); +} + diff --git a/content_types/node_form.inc b/content_types/node_form.inc new file mode 100644 index 0000000000000000000000000000000000000000..a7df3f6eebe553f2cd1660f53efe107364c64a2d --- /dev/null +++ b/content_types/node_form.inc @@ -0,0 +1,303 @@ + t('Node form'), + 'content_types' => 'panels_admin_content_types_node_form', + 'render callback' => 'panels_content_node_form', + 'add callback' => 'panels_admin_edit_node_form', + 'edit callback' => 'panels_admin_edit_node_form', + 'title callback' => 'panels_admin_title_node_form', + ); + return $items; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_node_form() { + $content = array( + 'publishing' => array( + 'title' => t('Node form publishing options'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Publishing options on the Node form.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ), + 'author' => array( + 'title' => t('Node form author information'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Author information on the Node form.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ), + 'input_format' => array( + 'title' => t('Node form input format'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Input format for the body field on a node.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ), + 'log' => array( + 'title' => t('Node form log message'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Log message for the node.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ), + ); + if (module_exists('comment')) { + $content['comment'] = array( + 'title' => t('Node form comment settings'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Comment settings on the Node form.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ); + } + if (module_exists('menu')) { + $content['menu'] = array( + 'title' => t('Node form menu settings'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('menu settings on the Node form.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ); + } + if (module_exists('path')) { + $content['url_path'] = array( + 'title' => t('URL Path settings'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Publishing options on the Node form.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ); + } + if (module_exists('upload')) { + $content['attachments'] = array( + 'title' => t('Node form file attachments'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('File attachments on the Node form.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ); + } + if (module_exists('taxonomy')) { + $content['taxonomy'] = array( + 'title' => t('Node form categories'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Taxonomy categories for the node.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ); + } + if (module_exists('book')) { + $content['book'] = array( + 'title' => t('Node form book options'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Book options for the node.'), + 'required context' => new panels_required_context(t('Form'), 'node_form'), + 'category' => array(t('Form'), -9), + ); + } + return $content; +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_node_form($conf, $panel_args, &$context) { + $block = new stdClass(); + $block->module = 'node-form'; + + switch ($conf['id']) { + case 'publishing': + $block->subject = t('Publishing options'); + $block->delta = 'publishing-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id) && $context->form['options']['#type'] == 'fieldset') { + // remove the fieldset + unset($context->form['options']['#type']); + $block->content = drupal_render($context->form['options']); + } + } + else { + $block->content = t('Publishing options.'); + } + break; + + case 'comment': + $block->subject = t('Comment options'); + $block->delta = 'comment-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id) && !empty($context->form['comment_settings']['#access'])) { + // remove the fieldset + unset($context->form['comment_settings']['#type']); + $block->content = drupal_render($context->form['comment_settings']); + } + } + else { + $block->content = t('Comment options.'); + } + break; + + case 'author': + $block->subject = $block->content = t('Authoring information'); + $block->delta = 'author-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id) && !empty($context->form['author']['#access'])) { + // remove the fieldset + unset($context->form['author']['#type']); + $context->form['author']['name']['#size'] /= 2; + $context->form['author']['date']['#size'] /= 2; + $block->content = drupal_render($context->form['author']); + } + } + else { + $block->content = t('Authoring information.'); + } + break; + + case 'menu': + $block->subject = t('Menu options'); + $block->delta = 'menu-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id) && !empty($context->form['menu']['#access'])) { + // remove the fieldset + unset($context->form['menu']['#type']); + $context->form['menu']['title']['#size'] /= 2; + $context->form['menu']['description']['#size'] /= 2; + $block->content = drupal_render($context->form['menu']); + } + } + else { + $block->content = t('Menu options.'); + } + break; + + case 'url_path': + $block->subject = t('URL path options'); + $block->delta = 'url-path-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id) && !empty($context->form['path']['#access'])) { + // remove the fieldset + unset($context->form['path']['#type']); + $context->form['path']['path']['#size'] /= 2; + $block->content = drupal_render($context->form['path']); + } + } + else { + $block->content = t('URL Path options.'); + } + break; + + case 'attachments': + $block->subject = t('Attach files'); + $block->delta = 'url-path-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id) && !empty($context->form['attachments']['#access'])) { + // remove the fieldset + unset($context->form['attachments']['#type']); + $block->content = drupal_render($context->form['attachments']); + } + } + else { + $block->content = t('Attach files.'); + } + break; + + case 'taxonomy': + $block->subject = t('Categories'); + $block->delta = 'url-path-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id) && !empty($context->form['taxonomy'])) { + // remove the fieldset + unset($context->form['taxonomy']['#type']); + $block->content = drupal_render($context->form['taxonomy']); + } + } + else { + $block->content = t('Categories.'); + } + break; + + case 'book': + $block->subject = t('Book options'); + $block->delta = 'book-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id)) { + $block->content = ''; + if ($context->form['parent']['#type'] != 'value') { + $block->content .= drupal_render($context->form['parent']); + } + if ($context->form['weight']['#type'] != 'value') { + $block->content .= drupal_render($context->form['weight']); + } + } + } + else { + $block->content = t('Book options.'); + } + break; + + case 'input_format': + $block->subject = t('Input format'); + $block->delta = 'format-options'; + + if (isset($context->form)) { + if (!empty($context->form->form_id) && !empty($context->form['body_filter']['format'])) { + // remove the fieldset + unset($context->form['body_filter']['format']['#type']); + $block->content = drupal_render($context->form['body_filter']['format']); + } + } + else { + $block->content = t('Input format.'); + } + break; + } + + return $block; +} + +/** + * Returns an edit form for the custom type. + */ +function panels_admin_edit_node_form($id, $parents, $conf = array()) { + $form['id'] = array( + '#type' => 'value', + '#value' => $id, + ); + + return $form; +} + +function panels_admin_title_node_form($conf, $context) { + $choices = panels_admin_content_types_node_form(); + return t('"@s" @type', array('@s' => $context->identifier, '@type' => drupal_strtolower($choices[$conf['id']]['title']))); +} + diff --git a/content_types/node_type_desc.inc b/content_types/node_type_desc.inc new file mode 100644 index 0000000000000000000000000000000000000000..c831c2742f52b9458ecf3d2646a8e8f94340c724 --- /dev/null +++ b/content_types/node_type_desc.inc @@ -0,0 +1,65 @@ + t('Node type description'), + 'content_types' => 'panels_admin_content_types_node_type_desc', + // only provides a single content type + 'single' => TRUE, + 'render callback' => 'panels_content_node_type_desc', + 'add callback' => 'panels_admin_edit_node_type_desc', + 'edit callback' => 'panels_admin_edit_node_type_desc', + 'title callback' => 'panels_admin_title_node_type_desc', + ); + return $items; +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_node_type_desc($conf, $panel_args, $context) { + $node = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'node_type'; + + if ($node) { + $type = node_get_types('type', $node); + $block->subject = $type->name; + $block->content = filter_xss_admin($type->description); + $block->delta = $node->type; + } + else { + $block->subject = t('Node type description'); + $block->content = t('Node type description goes here.'); + $block->delta = 'unknown'; + } + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_node_type_desc() { + return array( + 'node_type' => array( + 'title' => t('Node type description'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Node type description.'), + 'required context' => new panels_required_context(t('Node'), 'node'), + 'category' => array(t('Node context'), -9), + ), + ); +} + +function panels_admin_title_node_type_desc($conf, $context) { + return t('"@s" type description', array('@s' => $context->identifier)); +} + diff --git a/content_types/profile_fields.inc b/content_types/profile_fields.inc new file mode 100644 index 0000000000000000000000000000000000000000..5b975663863af4bca33eb6b7dee311a7ae9ed1c3 --- /dev/null +++ b/content_types/profile_fields.inc @@ -0,0 +1,153 @@ + t('Profile Fields'), + 'content_types' => 'panels_profile_fields_content_type', + // only provides a single content type + 'single' => TRUE, + 'render callback' => 'panels_profile_fields_content', + 'add callback' => 'panels_profile_fields_configure', + 'edit callback' => 'panels_profile_fields_configure', + 'title callback' => 'panels_profile_fields_configure_title', + ); + } + return $items; +} + +/** + * 'Render' callback for the 'profile fields' content type. + */ +function panels_profile_fields_content($conf, $panel_args, $context) { + $account = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'profile fields'; + + if ($account) { + // Get the category from the options + $category = str_replace("_", " ", $conf['category']); + + // Set the subject to the name of the category + $block->subject = $category; + + // Put all the fields in the category into an array + $profile = profile_view_profile($account); + if (is_array($profile[$category])) { + foreach ($profile[$category] as $field) { + $vars[$field['class']]['title'] = $field['title']; + $vars[$field['class']]['value'] = $field['value']; + } + } + + if (sizeof($vars) == 0) { + // Output the given empty text + $output = $conf['empty']; + } + else { + // Call the theme function with the field vars + $output = theme('profile_fields_pane', $category, $vars); + } + + $block->content = $output; + $block->delta = $account->uid; + } + else { + $block->subject = $conf['category']; + $block->content = t('Profile content goes here.'); + $block->delta = 'unknown'; + } + + return $block; +} + +/** + * Theme the profile fields retrieved in panels_profile_fields_content + */ +function theme_profile_fields_pane($category, $vars) { + if (is_array($vars)) { + foreach ($vars as $class => $field) { + $output .= '
'; + $output .= '
' . $field['title'] . '
'; + $output .= '
' . $field['value'] . '
'; + $output .= '
'; + } + } + + return $output; +} + +/** + * Return all content types available. + */ +function panels_profile_fields_content_type() { + return array( + 'description' => array( + 'title' => t('Profile Category'), + 'icon' => 'icon_user.png', + 'path' => panels_get_path('content_types/user'), + 'description' => t('Profile category contents.'), + 'required context' => new panels_required_context(t('User'), 'user'), + 'category' => array(t('User context'), -9), + ), + ); +} + +/** + * Helper function : build the list of categories for the 'edit' form. + */ +function _panels_profile_fields_options() { + $cat_list = array(); + + $categories = profile_categories(); + foreach ($categories as $key => $value) { + $cat_list[str_replace(" ", "_", $value['name'])] = $value['title']; + } + + return $cat_list; +} + +/** + * 'Edit' callback for the 'profile fields' content type. + */ +function panels_profile_fields_configure($id, $parents, $conf = array()) { + // Apply defaults + if (empty($conf)) { + $conf = array('title' => '', 'category' => '', 'empty' => ''); + } + + $form['category'] = array( + '#type' => 'radios', + '#title' => t('Which category'), + '#options' => _panels_profile_fields_options(), + '#default_value' => $conf['category'], + '#prefix' => '
', + '#suffix' => '
', + ); + + $form['empty'] = array( + '#type' => 'textarea', + '#title' => 'Empty text', + '#description' => t('Text to display if category has no data. Note that title will not display unless overridden.'), + '#rows' => 5, + '#default_value' => $conf['empty'], + '#prefix' => '
', + '#suffix' => '
', + ); + + return $form; +} + +/** + * 'Title' callback for the 'profile fields' content type. + */ +function panels_profile_fields_configure_title($conf, $context) { + return t('"@s" profile fields', array('@s' => $conf['category'])); +} + diff --git a/content_types/term_description.inc b/content_types/term_description.inc new file mode 100644 index 0000000000000000000000000000000000000000..70ea7660c3e7d02f7f00e78638a901dbc19429b9 --- /dev/null +++ b/content_types/term_description.inc @@ -0,0 +1,72 @@ + t('Term description'), + 'content_types' => 'panels_admin_content_types_term_description', + // only provides a single content type + 'single' => TRUE, + 'render callback' => 'panels_content_term_description', + 'add callback' => 'panels_admin_edit_term_description', + 'edit callback' => 'panels_admin_edit_term_description', + 'title callback' => 'panels_admin_title_term_description', + ); + return $items; +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_term_description($conf, $panel_args, $context) { + $term = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'node_type'; + + $block->subject = $term->name; + if ($term) { + $block->content = _filter_autop(filter_xss_admin($term->description)); + $block->delta = $term->tid; + + if (user_access('administer taxonomy')) { + $block->admin_links['update'] = array( + 'title' => t('Edit term'), + 'alt' => t("Edit this term"), + 'href' => "admin/content/taxonomy/edit/term/$term->tid", + 'query' => drupal_get_destination(), + ); + } + } + else { + $block->content = t('Term description goes here.'); + $block->delta = 'unknown'; + } + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_term_description() { + return array( + 'description' => array( + 'title' => t('Term description'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Term description.'), + 'required context' => new panels_required_context(t('Term'), 'term'), + 'category' => array(t('Term context'), -9), + ), + ); +} + +function panels_admin_title_term_description($conf, $context) { + return t('"@s" term description', array('@s' => $context->identifier)); +} + diff --git a/content_types/term_list.inc b/content_types/term_list.inc new file mode 100644 index 0000000000000000000000000000000000000000..4f1d3b2816b540d1722d303e8621acb0e6d0b4ae --- /dev/null +++ b/content_types/term_list.inc @@ -0,0 +1,134 @@ + t('List of related terms'), + 'content_types' => 'panels_admin_content_types_term_list', + // only provides a single content type + 'single' => TRUE, + 'render callback' => 'panels_content_term_list', + 'add callback' => 'panels_admin_edit_term_list', + 'edit callback' => 'panels_admin_edit_term_list', + 'title callback' => 'panels_admin_title_term_list', + ); + return $items; +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_term_list($conf, $panel_args, $context) { + $term = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'term-list'; + + $options = panels_admin_term_list_options(); + if ($term) { + $block->subject = $options[$conf['type']]; + $block->delta = $conf['type']; + switch ($conf['type']) { + case 'related': + $terms = taxonomy_get_related($term->tid); + break; + + case 'child': + default: + $terms = taxonomy_get_children($term->tid); + break; + + case 'top': + $terms = taxonomy_get_children(0, $term->vid); + break; + + case 'sibling': + $parent = db_result(db_query("SELECT parent FROM {term_hierarchy} WHERE tid = %d", $term->tid)); + $terms = taxonomy_get_children($parent, $term->vid); + // Remove the term that started this. + unset($terms[$term->tid]); + break; + + case 'synonyms': + $terms = taxonomy_get_synonyms($term->tid); + break; + } + if ($terms) { + foreach ($terms as $related) { + $items[$related->tid] = l($related->name, taxonomy_term_path($related), array('rel' => 'tag', 'title' => strip_tags($related->description))); + } + + $block->content = theme('item_list', $items, NULL, $conf['list_type']); + } + } + else { + $block->content = t('Term description goes here.'); + $block->delta = 'unknown'; + } + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_term_list() { + return array( + 'description' => array( + 'title' => t('List of related terms'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('Terms related to an existing term; may be child, siblings or top level.'), + 'required context' => new panels_required_context(t('Term'), 'term'), + 'category' => array(t('Term context'), -9), + ), + ); +} + +function panels_admin_term_list_options() { + return array( + 'child' => t('Child terms'), + 'related' => t('Related terms'), + 'sibling' => t('Sibling terms'), + 'top' => t('Top level terms'), + 'synonyms' => t('Term synonyms'), + ); +} + +/** + * Returns an edit form for the custom type. + */ +function panels_admin_edit_term_list($id, $parents, $conf = array()) { + // Apply defaults + if (empty($conf)) { + $conf = array('title' => '', 'type' => 'child', 'list_type' => 'ul'); + } + + $form['type'] = array( + '#type' => 'radios', + '#title' => t('Which terms'), + '#options' => panels_admin_term_list_options(), + '#default_value' => $conf['type'], + '#prefix' => '
', + '#suffix' => '
', + ); + + $form['list_type'] = array( + '#type' => 'select', + '#title' => t('List type'), + '#options' => array('ul' => t('Unordered'), 'ol' => t('Ordered')), + '#default_value' => $conf['list_type'], + ); + + return $form; +} + +function panels_admin_title_term_list($conf, $context) { + $options = panels_admin_term_list_options(); + return t('"@s" @type', array('@s' => $context->identifier, '@type' => drupal_strtolower($options[$conf['type']]))); +} + diff --git a/content_types/user_picture.inc b/content_types/user_picture.inc new file mode 100644 index 0000000000000000000000000000000000000000..03f447c6930502e334569a152692e6755edca6a0 --- /dev/null +++ b/content_types/user_picture.inc @@ -0,0 +1,60 @@ + t('User picture'), + 'content_types' => 'panels_admin_content_types_user_picture', + 'single' => TRUE, + 'render callback' => 'panels_content_user_picture', + 'title callback' => 'panels_admin_title_user_picture', + ); + return $items; +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_user_picture($conf, $panel_args, $context) { + $account = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'term-list'; + + if ($account === FALSE || ($account->access == 0 && !user_access('administer users'))) { + return drupal_not_found(); + } + + $block->title = check_plain($account->name); + $block->content = theme('user_picture', $account); + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_user_picture() { + return array( + 'description' => array( + 'title' => t('User picture'), + 'icon' => 'icon_user.png', + 'path' => panels_get_path('content_types/user'), + 'description' => t('The picture of a user.'), + 'required context' => new panels_required_context(t('User'), 'user'), + 'category' => array(t('User context'), -9), + ), + ); +} + +/** + * Display the administrative title for a panel pane in the drag & drop UI + */ +function panels_admin_title_user_picture($conf, $context) { + return t('"@s" user picture', array('@s' => $context->identifier)); +} + diff --git a/content_types/user_profile.inc b/content_types/user_profile.inc new file mode 100644 index 0000000000000000000000000000000000000000..8e8e8356fa7cd286efb8cbde2fb187cd7d4b81b2 --- /dev/null +++ b/content_types/user_profile.inc @@ -0,0 +1,80 @@ + t('User profile'), + 'content_types' => 'panels_admin_content_types_user_profile', + 'single' => TRUE, + 'render callback' => 'panels_content_user_profile', + 'title callback' => 'panels_admin_title_user_profile', + ); + return $items; +} + +/** + * Output function for the 'node' content type. Outputs a node + * based on the module and delta supplied in the configuration. + */ +function panels_content_user_profile($conf, $panel_args, $context) { + $account = isset($context->data) ? drupal_clone($context->data) : NULL; + $block = new stdClass(); + $block->module = 'term-list'; + + if ($account === FALSE || ($account->access == 0 && !user_access('administer users'))) { + return drupal_not_found(); + } + // Retrieve and merge all profile fields: + $fields = array(); + foreach (module_list() as $module) { + if ($data = module_invoke($module, 'user', 'view', '', $account)) { + foreach ($data as $category => $items) { + foreach ($items as $key => $item) { + $item['class'] = "$module-". $item['class']; + $fields[$category][$key] = $item; + } + } + } + } + + // Let modules change the returned fields - useful for personal privacy + // controls. Since modules communicate changes by reference, we cannot use + // module_invoke_all(). + foreach (module_implements('profile_alter') as $module) { + $function = $module .'_profile_alter'; + $function($account, $fields); + } + + $block->title = check_plain($account->name); + $block->content = theme('user_profile', $account, $fields); + + return $block; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_user_profile() { + return array( + 'description' => array( + 'title' => t('User profile'), + 'icon' => 'icon_user.png', + 'path' => panels_get_path('content_types/user'), + 'description' => t('The profile of a user.'), + 'required context' => new panels_required_context(t('User'), 'user'), + 'category' => array(t('User context'), -9), + ), + ); +} + +/** + * Display the administrative title for a panel pane in the drag & drop UI + */ +function panels_admin_title_user_profile($conf, $context) { + return t('"@s" user profile', array('@s' => $context->identifier)); +} + diff --git a/content_types/views.inc b/content_types/views.inc index e893d6c356a087c6bce53a8c4302f3a193d4df78..32c5665159f52c03a7e7dfd63a3a380ab2767b2e 100644 --- a/content_types/views.inc +++ b/content_types/views.inc @@ -1,154 +1,5 @@ - 'panels_content_views', - 'admin' => 'panels_admin_views', - ); - return $items; - } - - /** - * Output function for the 'views' content type. Outputs a views - * based on the module and delta supplied in the configuration. - */ - function panels_content_views($conf) { - $view = views_get_view($conf['view']); - if ($view) { - $arguments = explode('/', $_GET['q']); - $args = $conf['args']; - - foreach ($arguments as $id => $arg) { - $args = str_replace("%$id", $arg, $args); - } - - $args = preg_replace('/\%\d/', '', $args); - $args = $args ? explode('/', $args) : array(); - - if ($conf['url']) { - $view->url = $conf['url']; - } - - $output = views_build_view($conf['type'], $view, $args, intval($conf['pager_id']), intval($conf['nodes_per_page'])); - } - return $output; - } - - /** - * Callback to perform administrative functions on the content views - */ - function panels_admin_views($op, &$arg, $arg2 = NULL) { - switch ($op) { - case 'list': - $conf = $arg; - $view = views_get_view($conf['view']); - return 'Views: ' . $view->name . ' (' . $view->description . ')'; - case 'add button': - $result = db_query("SELECT name, description FROM {view_view}"); - while ($view = db_fetch_object($result)) { - $views[$view->name] = $view->name . ': ' . $view->description; - } - - $default_views = _views_get_default_views(); - $views_status = variable_get('views_defaults', array()); - foreach ($default_views as $view) { - if (!$views[$view->name] && - ($views_status[$view->name] == 'enabled' || (!$views_status[$view->name] && !$view->disabled))) { - $views[$view->name] = check_plain($view->name . ': ' . $view->description); - } - } - - ksort($views); - - $form['view'] = array( - '#type' => 'select', - '#options' => $views, - '#title' => t('Choose a view from the views module'), - ); - $form['submit'] = array( - '#type' => 'button', - '#value' => t('Add view'), - ); - - $form['#prefix'] = '
'; - $form['#suffix'] = '
'; - return $form; - case 'add': - if ($_POST['op'] != t('Add view')) { - return; - } - $conf = $arg; - $view = views_get_view($conf['view']); - if ($view->page) { - $conf['type'] = 'page'; - $conf['nodes_per_page'] = $view->nodes_per_page; - } - else { - $conf['type'] = 'block'; - $conf['nodes_per_page'] = $view->nodes_per_block; - } - $conf['pager_id'] = 0; - return $conf; - case 'edit': - $conf = &$arg; - $form['view'] = array( - '#type' => 'hidden', - '#default_value' => $conf['view'], - ); - $form['type'] = array( - '#type' => 'select', - '#default_value' => $conf['type'], - '#title' => t('View type'), - '#description' => t('Select which type of the view to display.'), - '#options' => array('page' => t('Page'), 'block' => t('Block'), 'embed' => t('Embedded')), - ); - $form['pager_id'] = array( - '#type' => 'textfield', - '#default_value' => $conf['pager_id'], - '#title' => t('Pager ID'), - '#size' => 4, - '#description' => t('Select the numeric pager ID to use, or 0 to not have use paging. Select "1" if you aren\'t sure what this means'), - ); - - $form['nodes_per_page'] = array( - '#type' => 'textfield', - '#default_value' => $conf['nodes_per_page'], - '#title' => t('Posts to Display'), - '#size' => 4, - '#description' => t('Select the number of posts to display, or 0 to display all results.'), - ); - - $form['args'] = array( - '#type' => 'textfield', - '#default_value' => $conf['args'], - '#title' => t('View arguments'), - '#size' => 12, - '#description' => t('Arguments to send to the view as if they were part of the URL in the form of arg1/arg2/arg3. You can use %0, %1, %2, etc, to use arguments from the actual URL. For example, if your panel URL is foo/bar, and someone hits foo/bar/5 use %2 to get the 5.'), - ); - - $form['url'] = array( - '#type' => 'textfield', - '#default_value' => $conf['url'], - '#title' => t('Override URL'), - '#size' => 12, - '#description' => t('If this is set, override the View URL; this can sometimes be useful to set to the panel URL'), - ); - - return $form; - case 'validate': - // This one has nothing to validate. - $form_values = &$arg; - $form = $arg2; - return; - case 'save': - // For this one, the form values go directly into the config. - $form = &$arg; - return $form; - } - } - -} \ No newline at end of file + t('Term description'), + 'content_types' => 'panels_admin_content_types_vocabulary_terms', + 'single' => TRUE, + 'render callback' => 'panels_content_vocabulary_terms', + 'add callback' => 'panels_admin_edit_vocabulary_terms', + 'edit callback' => 'panels_admin_edit_vocabulary_terms', + 'title callback' => 'panels_admin_title_vocabulary_terms', + ); + return $items; + } +} + +/** + * Output function for the 'vocabulary terms' content type. Outputs a + * list of terms for the input vocabulary. + */ +function panels_content_vocabulary_terms($conf, $panel_args, $context) { + $vocab = isset($context->data) ? drupal_clone($context->data) : NULL; + $max_depth = (!empty($conf['max_depth']) ? (int)$conf['max_depth'] : NULL); + if ($conf['tree'] == FALSE) { + $terms = taxonomy_get_tree($vocab->vid, 0, -1, $max_depth); + $items = array(); + foreach ($terms as $term) { + $items[] = l(check_plain($term->name), 'taxonomy/term/'. $term->tid); + } + $output = theme('item_list', $items); + } + else { + $output = theme('item_list', _panels_content_vocabulary_terms($vocab->vid, $max_depth)); + } + + $block = new stdClass(); + $block->module = 'node_type'; + $block->subject = $vocab->name; + $block->content = $output; + $block->delta = $vocab->tid; + + return $block; +} + +function _panels_content_vocabulary_terms($vid, $max_depth, $depth = -1, $tid = 0) { + $depth++; + if ($max_depth != NULL && $depth == $max_depth) { + return array(); + } + $return = array(); + $query = db_query('SELECT t.name, t.tid FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d AND h.parent = %d ORDER BY t.weight ASC, t.name ASC', $vid, $tid); + while ($result = db_fetch_object($query)) { + $return[] = array( + 'data' => l($result->name, 'taxonomy/term/'. $result->tid), + 'children' => _panels_content_vocabulary_terms($vid, $max_depth, $depth, $result->tid), + ); + } + return $return; +} + +/** + * Return all content types available. + */ +function panels_admin_content_types_vocabulary_terms() { + return array( + 'description' => array( + 'title' => t('Vocabulary terms'), + 'icon' => 'icon_node.png', + 'path' => panels_get_path('content_types/node'), + 'description' => t('All the terms in a vocabulary.'), + 'required context' => new panels_required_context(t('Vocabulary'), 'vocabulary'), + 'category' => array(t('Vocabulary context'), -9), + ), + ); +} + +function panels_admin_title_vocabulary_terms($conf, $context) { + return t('"@s" terms', array('@s' => $context->identifier)); +} + +function panels_admin_edit_vocabulary_terms($id, $parents, $conf = array()) { + // Apply defaults + if (empty($conf)) { + $conf = array('max_depth' => 0, 'tree' => 1); + } + + $form['max_depth'] = array( + '#type' => 'select', + '#title' => t('Maximum depth'), + '#options' => array_merge(array(t('unlimited')), range(1, 9)), + '#default_value' => $conf['max_depth'], + '#description' => t('Define the maximum depth of terms being displayed.'), + ); + + $form['tree'] = array( + '#type' => 'checkbox', + '#title' => t('Display as tree'), + '#default_value' => $conf['tree'], + '#description' => t('If checked, the terms are displayed in a tree, otherwise in a flat list.'), + ); + + return $form; +} + diff --git a/images/blank.gif b/images/blank.gif index c31552c825663a028bc9b69dad6cd3b920052f38..75b945d2553848b8b6f41fe5e24599c0687b8472 100644 Binary files a/images/blank.gif and b/images/blank.gif differ diff --git a/images/close.gif b/images/close.gif new file mode 100644 index 0000000000000000000000000000000000000000..46891b0c0d4871eb17b8e71690f42c6eae76bd7c Binary files /dev/null and b/images/close.gif differ diff --git a/images/delete.png b/images/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..f790555163b1abcad7e350282a47ef8f77d8ec4a Binary files /dev/null and b/images/delete.png differ diff --git a/images/go-bottom.png b/images/go-bottom.png deleted file mode 100644 index 741b6191fe7aa90d935d44be384913602734659a..0000000000000000000000000000000000000000 Binary files a/images/go-bottom.png and /dev/null differ diff --git a/images/go-down.png b/images/go-down.png index 30acf6863f2ed514ad7aefe7d923ad9c74342e8c..c2def1a3124f8aa309bdcd7a0b3aef6c191b4616 100644 Binary files a/images/go-down.png and b/images/go-down.png differ diff --git a/images/go-right.png b/images/go-right.png new file mode 100644 index 0000000000000000000000000000000000000000..dd6058c8652f419bbef9f6c3259c080bcf6ab364 Binary files /dev/null and b/images/go-right.png differ diff --git a/images/go-top.png b/images/go-top.png deleted file mode 100644 index e6f9532e120166efa448eaa722895ffa459f194b..0000000000000000000000000000000000000000 Binary files a/images/go-top.png and /dev/null differ diff --git a/images/go-up.png b/images/go-up.png index 17b374f91b2ab67d1549972674885beed2d9d8f9..a52c7dcb66d2be7169372a149058892482eccc6c 100644 Binary files a/images/go-up.png and b/images/go-up.png differ diff --git a/images/icon-addcontent.png b/images/icon-addcontent.png new file mode 100644 index 0000000000000000000000000000000000000000..788d01ff2574383caf22d8e553949fe3c4b95468 Binary files /dev/null and b/images/icon-addcontent.png differ diff --git a/images/icon-cache.png b/images/icon-cache.png new file mode 100644 index 0000000000000000000000000000000000000000..3e6f46b3fb5586a57d939939536f484a67dcda19 Binary files /dev/null and b/images/icon-cache.png differ diff --git a/images/icon-configure.png b/images/icon-configure.png new file mode 100644 index 0000000000000000000000000000000000000000..e23d67cc04b84880d0437e23ffcba837d2dc4121 Binary files /dev/null and b/images/icon-configure.png differ diff --git a/images/icon-delete.png b/images/icon-delete.png new file mode 100644 index 0000000000000000000000000000000000000000..5f0cf695b0cac487efecfd7eae66e7492a2ba306 Binary files /dev/null and b/images/icon-delete.png differ diff --git a/images/icon-draggable.png b/images/icon-draggable.png new file mode 100644 index 0000000000000000000000000000000000000000..dba8b67cdf37714c933bc9514cfdf5e35df89fb3 Binary files /dev/null and b/images/icon-draggable.png differ diff --git a/images/icon-hidepane.png b/images/icon-hidepane.png new file mode 100644 index 0000000000000000000000000000000000000000..851698056e69dd3058fe1cc0456064ebb49909be Binary files /dev/null and b/images/icon-hidepane.png differ diff --git a/images/icon-showpane.png b/images/icon-showpane.png new file mode 100644 index 0000000000000000000000000000000000000000..7549dd94d646ef74cb640beeeb911bfec9895bf5 Binary files /dev/null and b/images/icon-showpane.png differ diff --git a/images/no-icon.png b/images/no-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..30584e6f2540ad49dac98060d1a7f8a5af1b27b7 Binary files /dev/null and b/images/no-icon.png differ diff --git a/images/portlet-collapsed.png b/images/portlet-collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..95a214a6e6d17fee2f098804997f3826ffc9d4ca Binary files /dev/null and b/images/portlet-collapsed.png differ diff --git a/images/portlet-expanded.png b/images/portlet-expanded.png new file mode 100644 index 0000000000000000000000000000000000000000..46f39ecb351cff65243fa9a614a69d039e1302a5 Binary files /dev/null and b/images/portlet-expanded.png differ diff --git a/images/throbber.gif b/images/throbber.gif new file mode 100644 index 0000000000000000000000000000000000000000..8a084b8447d506e9b655ad52405cbf7a73034550 Binary files /dev/null and b/images/throbber.gif differ diff --git a/layouts/flexible.css b/layouts/flexible.css new file mode 100644 index 0000000000000000000000000000000000000000..fe12ca5a25d6f28c0bc2b7fb0ed91ff3196af31a --- /dev/null +++ b/layouts/flexible.css @@ -0,0 +1,58 @@ +/* $Id$ */ + +.panel-flexible { +/* overflow: hidden; */ +} + +.panel-flexible { + width: 99.9%; +} + +.panel-flexible .panel-col { + float: left; + padding: 0; + margin: 1px 0 .5em 0; +} + +.panel-flexible .panel-row { + padding: 0; + margin: 0; + padding-bottom: .5em; + width: 99.9%; +} + +.panel-flexible .panel-col-first .inside { + padding-right: .5em; +} + +.panel-flexible .panel-col-inside .inside { + padding-right: .5em; + padding-left: .5em; +} + +.panel-flexible .panel-col-last .inside { + padding-left: .5em; +} + +#panels-edit-display .panel-pane, +#panels-edit-display .helperclass { + margin: .5em; +} + +.panel-flexible .panel-separator { + margin: 0 0 1em 0; +} + +/* sidebar css */ +div.panel-flexible div.panel-flexible-sidebars div.panel-sidebar { + float: left; + position: relative; +} + +* html div.panel-sidebar-left div.inside { + background-color: transparent; +} + +div.panel-flexible div.panel-flexible-sidebars div.panel-sidebar-middle { + width: 100%; +} diff --git a/layouts/flexible.inc b/layouts/flexible.inc new file mode 100644 index 0000000000000000000000000000000000000000..e840a7db96ff0c6836e07918dc5988d1547b7d69 --- /dev/null +++ b/layouts/flexible.inc @@ -0,0 +1,366 @@ + t('Flexible'), + 'icon' => 'layouts/flexible.png', + 'theme' => 'panels_flexible', + 'theme arguments' => array('id', 'content', 'settings'), + 'css' => 'layouts/flexible.css', + 'settings form' => 'panels_flexible_settings_form', + 'settings submit' => 'panels_flexible_settings_submit', + 'panels function' => 'panels_flexible_panels', + ); + + return $items; +} + +function panels_flexible_default_panels() { + return array( + 'percent_width' => 100, + 'rows' => 3, + 'width_type' => '%', + 'row_1' => array( + 'columns' => 1, + 'width_1' => 100, + 'names' => array(t('Top')), + ), + 'row_2' => array( + 'columns' => 3, + 'width_1' => 25, + 'width_2' => 50, + 'width_3' => 25, + 'names' => array(t('Left'), t('Middle'), t('Right')), + ), + // row 3 + 'row_3' => array( + 'columns' => 1, + 'width_1' => 100, + 'names' => array(t('Bottom')), + ), + 'sidebars' => array( + 'left' => FALSE, + 'left_width' => 200, + 'right' => FALSE, + 'right_width' => 200, + 'width_type' => 'px', + ), + ); +} + +function panels_flexible_settings_form($display, $layout, $settings) { + if (empty($settings)) { + // default for a new flexible layout + $settings = panels_flexible_default_panels(); + } + + // Special check for updating: + if (empty($settings['width_type'])) { + $settings['width_type'] = '%'; + $settings['percent_width'] = 100; + $settings['sidebars']['left'] = FALSE; + $settings['sidebars']['left_width'] = 200; + $settings['sidebars']['right'] = FALSE; + $settings['sidebars']['right_width'] = 200; + $settings['sidebars']['width_type'] = 'px'; + } + + $form['instructions'] = array( + '#value' => t('

Here you may determine the number of rows and columns your layout may have. Each row can have its own number of columns, and each column can have its width set independently. When changing the number of rows or columns, click Save to update the form so you can set the widths for new cells properly.

Note: Removing cells which contain panes will cause those panes to be disappear. Please move any content you wish to keep.

'), + ); + + $form['width_type'] = array( + '#type' => 'select', + '#title' => t('Width unit type'), + '#options' => array('%' => t('% (percentage)'), 'px' => t('px (pixels)'), 'em' => t('em (current)')), + '#description' => t('The width unit type this layout can have: %, px or em. When using percentage, your layout will be fluid; when using px or em, your layout will be fixed.'), + '#default_value' => $settings['width_type'], + ); + + $form['percent_width'] = array( + '#type' => 'textfield', + '#size' => 2, + '#width' => 10, + '#title' => t('Total width'), + '#description' => t('If using the percentage width, choose the total width that this layout must add up to; if you are having problems with your flexible layout having columns fall off, try lowering this number and adjusting the width of individual columns to match.'), + '#default_value' => $settings['percent_width'], + ); + + $form['rows'] = array( + '#type' => 'textfield', + '#size' => 2, + '#width' => 10, + '#title' => t('Rows'), + '#description' => t('The number of rows this layout can have.'), + '#default_value' => $settings['rows'], + ); + + for ($row = 1; $row <= intval($settings['rows']); $row++) { + $form["row_$row"] = array( + '#type' => 'fieldset', + '#title' => t('Row @d', array('@d' => $row)), + ); + $form["row_$row"]["columns"] = array( + '#prefix' => '
', + '#suffix' => '
', + '#type' => 'textfield', + '#size' => 2, + '#width' => 10, + '#title' => t('Columns'), +// '#description' => t('The number of columns in row @d.', array('@d' => $row)), + '#default_value' => $settings["row_$row"]["columns"], + ); + for ($col = 1; $col <= intval($settings["row_$row"]["columns"]); $col++) { + $form["row_$row"]["width_$row_$col"] = array( + '#prefix' => '
', + '#suffix' => '
', + '#type' => 'textfield', + '#size' => 2, + '#width' => 10, + '#title' => t('Width @d', array('@d' => $col)), + '#default_value' => $settings["row_$row"]["width_$col"], + ); + } + if (is_array($settings["row_$row"]["names"])) { + $names = implode(', ', $settings["row_$row"]["names"]); + } + else { + $names = ''; + } + $form["row_$row"]['names'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#type' => 'textfield', + '#title' => t('Column titles, separated by commas'), + '#default_value' => $names, + ); + } + + $form['sidebars'] = array( + '#type' => 'fieldset', + '#title' => t('Sidebars'), + ); + + $form['sidebars']['left_aligner_start'] = array( + '#value' => '
', + '#tree' => TRUE, + ); + $form['sidebars']['left'] = array( + '#type' => 'checkbox', + '#id' => 'sidebar-left-checkbox', + '#title' => t('Sidebar left'), + '#default_value' => $settings['sidebars']['left'], + ); + $form['sidebars']['left_width'] = array( + '#type' => 'textfield', + '#id' => 'sidebar-left-width', + '#size' => 2, + '#width' => 10, + '#default_value' => $settings['sidebars']['left_width'], + ); + $form['sidebars']['right'] = array( + '#type' => 'checkbox', + '#id' => 'sidebar-right-checkbox', + '#title' => t('Sidebar right'), + '#default_value' => $settings['sidebars']['right'], + ); + + $form['sidebars']['right_width'] = array( + '#type' => 'textfield', + '#id' => 'sidebar-right-width', + '#size' => 2, + '#width' => 10, + '#default_value' => $settings['sidebars']['right_width'], + ); + $form['sidebars']['left_aligner_stop'] = array( + '#value' => '
', + ); + $form['sidebars']['left_title_markup'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#value' => t('If a sidebar is selected, enter the width of the sidebar.'), + ); + + $form['sidebars']['width_type'] = array( + '#type' => 'select', + '#title' => t('Width unit type'), + '#options' => array('%' => t('% (percentage)'), 'px' => t('px (pixels)'), 'em' => t('em (current)')), + '#description' => t('The width unit type activated sidebars will have: %, px or em. When using percentage, your sidebars will be fluid; when using px or em, your sidebars will be fixed.'), + '#default_value' => $settings['sidebars']['width_type'], + ); + + $js_settings = array('panels' => array('checkboxes' => array( + '#sidebar-left-checkbox' => array('#sidebar-left-width'), + '#sidebar-right-checkbox' => array('#sidebar-right-width'), + ))); + drupal_add_js(panels_get_path('js/checkboxes.js')); + drupal_add_js($js_settings, 'setting'); + return $form; +} + +function panels_flexible_settings_validate($values, $form, $display, $layout, $settings) { + if ($values['rows'] < 1) { + form_error($form['rows'], t('Rows must be a positive integer.')); + return; + } + + // Validate that percentages add up to the stated maximum. + if ($settings['width_type'] == '%') { + for ($row = 1; $row <= intval($values['rows']); $row++) { + // This takes into account whether or even had a previous setting here. + if ($settings['rows'] >= $row) { + if ($values["row_$row"]['columns'] < 1) { + form_error($form["row_$row"]['columns'], t('Columns must be a positive integer.')); + return; + } + $total = 0; + for ($col = 1; $col <= intval($values["row_$row"]["columns"]); $col++) { + $total += $values["row_$row"]["width_$col"]; + } + if ($total != $settings['percent_width']) { + form_error($form["row_$row"]['columns'], t('Column widths must add up to 100.')); + } + } + } + } +} + +function panels_flexible_settings_submit(&$values, $display, $layout, $settings) { + for ($row = 1; $row <= $values['rows']; $row++) { + if ($row > $settings['rows'] && empty($values["row_$row"]['columns'])) { + $values["row_$row"]['columns'] = 1; + $values["row_$row"]['width_1'] = 100; + } + if (!empty($values["row_$row"]['names'])) { + $names = explode(',', $values["row_$row"]['names']); + foreach ($names as $nid => $name) { + $names[$nid] = trim($name); + } + $values["row_$row"]['names'] = $names; + } + } +} + +/** + * Define the actual list of columns and rows for this flexible panel. + */ +function panels_flexible_panels($display, $settings) { + $panels = array(); + if (empty($settings)) { + $settings = panels_flexible_default_panels(); + } + + if (!empty($settings['sidebars']['left'])) { + $panels['sidebar_left'] = t('Left sidebar'); + } + + if (!empty($settings['sidebars']['right'])) { + $panels['sidebar_right'] = t('Right sidebar'); + } + + for ($row = 1; $row <= intval($settings['rows']); $row++) { + for ($col = 1; $col <= intval($settings["row_$row"]['columns']); $col++) { + if (!empty($settings["row_$row"]['names'][$col - 1])) { + $panels["row_${row}_$col"] = $settings["row_$row"]['names'][$col - 1]; + } + else { + $panels["row_${row}_$col"] = t("Row @row, Column @col", array('@row' => $row, '@col' => $col)); + } + } + } + return $panels; +} + +/** + * This function uses heredoc notation to make it easier to convert + * to a template. + */ +function theme_panels_flexible($id, $content, $settings) { + if (empty($settings)) { + $settings = panels_flexible_default_panels(); + } + + // Special check for updating. + if (empty($settings['width_type'])) { + $settings['width_type'] = '%'; + $settings['percent_width'] = 100; + } + + if ($id) { + $idstr = " id='$id'"; + $idcss = "#$id"; + } + else { + $idcss = "div.panel-flexible"; + } + + $css = ''; + $output = ''; + + for ($row = 1; $row <= intval($settings['rows']); $row++) { + $output .= "
\n"; + for ($col = 1; $col <= intval($settings["row_$row"]["columns"]); $col++) { + // We do a width reduction formula to help IE out a little bit. If width is 100%, we take 1% + // off the total; by dividing by the # of columns, that gets us the reduction overall. + $reduce = 0; + if ($settings['width_type'] == '%' && $settings['percent_width'] == 100) { + $reduce = 1 / $settings["row_$row"]["columns"]; + } + if ($col == 1) { + if (intval($settings["row_$row"]["columns"]) == 1) { + $class = 'panel-col-only'; + } + else { + $class = 'panel-col-first'; + } + } + elseif ($col == intval($settings["row_$row"]["columns"])) { + $class = 'panel-col-last'; + } + else { + $class = 'panel-col-inside'; + } + $output .= "
\n"; + $output .= "
" . $content["row_${row}_$col"] . "
\n"; + $output .= "
\n"; // panel-col-$col + $css .= "$idcss div.panel-row-$row div.panel-col-$col { width: " . ((intval($settings["row_$row"]["width_$col"])) - $reduce) . $settings["width_type"] ."; }\n"; + } + $output .= "
\n"; // panel-row-$row + } + + // Add our potential sidebars + if (!empty($settings['sidebars']['left']) || !empty($settings['sidebars']['right'])) { + // provide a wrapper if we have a sidebar + $output = "
\n$output
\n"; + if ($settings['sidebars']['width_type'] == '%') { + $css .= "$idcss div.panel-flexible-sidebars div.panel-sidebar-middle { width: " . (intval($settings['percent_width']) - intval($settings['sidebars']['left_width']) - intval($settings['sidebars']['right_width'])) . "; }\n"; + } + } + + if (!empty($settings['sidebars']['left'])) { + $size = intval($settings['sidebars']['left_width']) . $settings['sidebars']['width_type']; + $output = "
\n" . $content["sidebar_left"] . "
\n
\n" . $output; + $css .= "$idcss div.panel-flexible-sidebars div.panel-sidebar-left { width: $size; margin-left: -$size; }\n"; + $css .= "$idcss div.panel-flexible-sidebars { padding-left: $size; }\n"; + // IE hack + $css .= "* html $idcss div.panel-flexible-sidebars div.panel-sidebar-left { left: $size; }\n"; + } + + if (!empty($settings['sidebars']['right'])) { + $size = intval($settings['sidebars']['right_width']) . $settings['sidebars']['width_type']; + $output .= "
\n" . $content["sidebar_right"] . "
\n
\n"; + $css .= "$idcss div.panel-flexible-sidebars div.panel-sidebar-right { width: $size; margin-right: -$size; }\n"; + $css .= "$idcss div.panel-flexible-sidebars { padding-right: $size; }\n"; + } + + // Wrap the whole thing up nice and snug + $output = "
\n
\n" . $output . "
\n
\n"; + drupal_set_html_head("\n"); + return $output; +} + diff --git a/layouts/flexible.png b/layouts/flexible.png new file mode 100644 index 0000000000000000000000000000000000000000..14b4779a0b4f7d3516092e6790310aa559c96fe9 Binary files /dev/null and b/layouts/flexible.png differ diff --git a/layouts/onecol.css b/layouts/onecol.css new file mode 100644 index 0000000000000000000000000000000000000000..c5a45e794ce978f3a8228e94b052818476858884 --- /dev/null +++ b/layouts/onecol.css @@ -0,0 +1,22 @@ +/* $Id$ */ + +.panel-1col { +/* overflow: hidden; */ +} + +.panel-2col .panel-col-first .inside { + margin: 0; +} + + +.panel-1col .panel-col { +} + +#panels-edit-display .panel-pane, +#panels-edit-display .helperclass { + margin: .5em; +} + +.panel-2col .panel-separator { + margin: 0 0 1em 0; +} diff --git a/layouts/onecol.inc b/layouts/onecol.inc new file mode 100644 index 0000000000000000000000000000000000000000..0e226702e8eefa9a2bfda967d301202f7d031324 --- /dev/null +++ b/layouts/onecol.inc @@ -0,0 +1,40 @@ + 'panels', + 'title' => t('Single column'), + 'icon' => 'layouts/onecol.png', + 'theme' => 'panels_onecol', + 'theme arguments' => array('id', 'content'), + 'css' => 'layouts/onecol.css', + 'panels' => array('middle' => t('Middle column')), + ); + + return $items; +} + +/** + * This function uses heredoc notation to make it easier to convert + * to a template. + */ +function theme_panels_onecol($id, $content) { + if ($id) { + $idstr = " id='$id'"; + } + + $output = << +
+
$content[middle]
+
+
+EOT; + return $output; +} + diff --git a/layouts/onecol.png b/layouts/onecol.png new file mode 100644 index 0000000000000000000000000000000000000000..176ed69e7f59d6705471a00705b143131cdc03a4 Binary files /dev/null and b/layouts/onecol.png differ diff --git a/layouts/twocol_bricks.css b/layouts/twocol_bricks.css new file mode 100644 index 0000000000000000000000000000000000000000..cc29e86e873ea903c1cd1ef9825fe039dc443f8f --- /dev/null +++ b/layouts/twocol_bricks.css @@ -0,0 +1,47 @@ +/* $Id$ */ + +.panel-2col-bricks { +/* overflow: hidden; */ + margin-top: 0; + padding-top: 0; +} + +.panel-2col-bricks .panel-col-top, +.panel-2col-bricks .panel-col-middle, +.panel-2col-bricks .panel-col-bottom { + width: 99.9%; + clear: both; +} + +.panel-2col-bricks .panel-col-top .inside, +.panel-2col-bricks .panel-col-middle .inside { + margin-bottom: .5em; +} + +.panel-2col-bricks .panel-col-first { + float: left; + width: 50%; +} +* html .panel-2col-bricks .panel-col-first { + width: 49.9%; +} + +.panel-2col-bricks .panel-col-first .inside { + margin: 0 .5em .5em 0; +} + +.panel-2col-bricks .panel-col-last { + float: left; + width: 50%; +} +* html .panel-2col-bricks .panel-col-last { + width: 49.9%; +} + +.panel-2col-bricks .panel-col-last .inside { + margin: 0 0 .5em .5em; +} + +.panel-2col-bricks .panel-separator { + margin: 0 0 1em 0; +} diff --git a/layouts/twocol_bricks.inc b/layouts/twocol_bricks.inc new file mode 100644 index 0000000000000000000000000000000000000000..412892662f33aa07d1413a6a5b314336e0e77ed4 --- /dev/null +++ b/layouts/twocol_bricks.inc @@ -0,0 +1,77 @@ + 'panels', + 'title' => t('Two column bricks'), + 'icon' => 'layouts/twocol_bricks.png', + 'theme' => 'panels_twocol_bricks', + 'theme arguments' => array('id', 'content'), + 'css' => 'layouts/twocol_bricks.css', + 'panels' => array( + 'top' => t('Top'), + 'left_above' => t('Left above'), + 'right_above' => t('Right above'), + 'middle' => t('Middle'), + 'left_below' => t('Left below'), + 'right_below' => t('Right below'), + 'bottom' => t('Bottom'), + ), + ); + return $items; +} + +/** + * This function uses heredoc notation to make it easier to convert + * to a template. + */ +function theme_panels_twocol_bricks($id, $content) { + if ($id) { + $idstr = " id='$id'"; + } + + $output = << +
+
$content[top]
+
+
+
+
$content[left_above]
+
+ +
+
$content[right_above]
+
+
+
+
$content[middle]
+
+
+
+
$content[left_below]
+
+ +
+
$content[right_below]
+
+
+
+
$content[bottom]
+
+ +EOT; + return $output; +} + diff --git a/layouts/twocol_bricks.png b/layouts/twocol_bricks.png new file mode 100644 index 0000000000000000000000000000000000000000..450395c801c960fdff5b5acb6100a314d7926042 Binary files /dev/null and b/layouts/twocol_bricks.png differ diff --git a/panels.info b/panels.info index 27aaac42f1221ce88fd2c225a6f2c0fc94059d70..2ae7690885bf7aaa2bd519c13715cf82a5610c25 100644 --- a/panels.info +++ b/panels.info @@ -1,4 +1,6 @@ ; $Id$ name = Panels -description = Create pages that are divided into areas of the page. +description = Core Panels display functions; provides no external UI, at least one other module (such as Panel pages) should be enabled. +core = 6.x +package = "Panels" diff --git a/panels.install b/panels.install index b519dfd96111fbbd2a191a591c1e37543d6fb976..4116e93b734e1a589683044da3a33991e7a49437 100644 --- a/panels.install +++ b/panels.install @@ -2,37 +2,149 @@ // $Id$ /** - * Install the panels tables + * Implementation of hook_schema(). */ -function panels_install() { - switch ($GLOBALS['dbtype']) { - case 'pgsql': - case 'mysql': - case 'mysqli': - default: - db_query(<< array( + 'did' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'layout' => array( + 'type' => 'varchar', + 'length' => '32', + ), + 'layout_settings' => array( + 'type' => 'text', + 'size' => 'big', + ), + 'panel_settings' => array( + 'type' => 'text', + 'size' => 'big', + ), + 'cache' => array( + 'type' => 'text', + ), + 'title' => array( + 'type' => 'varchar', + 'length' => '255', + ), + 'hide_title' => array( + 'type' => 'int', + 'size' => 'tiny', + ), + ), + 'primary key' => array('did'), + ); + + $schema['panels_pane'] = array( + 'fields' => array( + 'pid' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'did' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'panel' => array( + 'type' => 'varchar', + 'length' => '32', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => '32', + ), + 'subtype' => array( + 'type' => 'varchar', + 'length' => '64', + ), + 'shown' => array( + 'type' => 'int', + 'size' => 'tiny', + 'default' => 1, + ), + 'access' => array( + 'type' => 'varchar', + 'length' => '128', + ), + 'visibility' => array( + 'type' => 'text', + 'size' => 'big', + ), + 'configuration' => array( + 'type' => 'text', + 'size' => 'big', + ), + 'cache' => array( + 'type' => 'text', + 'size' => 'big', + ), + 'position' => array( + 'type' => 'int', + 'size' => 'small', + ), + ), + 'primary key' => array('pid'), + 'indexes' => array( + 'did_idx' => array('did') + ), + ); + + $schema['panels_object_cache'] = array( + 'fields' => array( + 'sid' => array( + 'type' => 'varchar', + 'length' => '64', + ), + 'did' => array( + 'type' => 'int', + ), + 'obj' => array( + 'type' => 'varchar', + 'length' => '255', + ), + 'data' => array( + 'type' => 'text', + 'size' => 'big', + ), + 'timestamp' => array( + 'type' => 'int', + ), + ), + 'indexes' => array( + 'idx' => array('sid', 'obj', 'did'), + 'time_idx' => array('timestamp') + ), ); + return $schema; +} - } +/** + * Implementation of hook_install(). + */ +function panels_install() { + db_query("UPDATE {system} SET weight = 10 WHERE name = 'panels'"); + drupal_install_schema('panels'); +} + +/** + * Implementation of hook_uninstall(). + */ +function panels_uninstall() { + drupal_uninstall_schema('panels'); } diff --git a/panels.module b/panels.module index 0ebefcc95f6160eb99f095bfc49b3253a5f1b6ad..c8804054ee5798c1ace1b62cb2f2989dc823cd91 100644 --- a/panels.module +++ b/panels.module @@ -2,797 +2,908 @@ // $Id$ /** - * Implementation of hook_help() + * Returns the API version of Panels. This didn't exist in 1. + * + * @return An array with the major and minor versions */ -function panels_help($section = '') { - switch ($section) { - case 'admin/build/panels': - case 'admin/build/panels/list': - return t('

You may peruse a list of your current panels layouts and edit them, or click add to create a new page.

'); - case 'admin/build/panels/add': - return t('

Choose a layout for your new page from the list below.

'); +function panels_api_version() { + return array(2, 0); +} + +function panels_theme() { + $theme = array(); + $theme['panels_layout_link'] = array( + 'arguments' => array('title', 'id', 'image', 'link'), + ); + $theme['panels_layout_icon'] = array( + 'arguments' => array('id', 'image', 'title' => NULL), + ); + $theme['panels_imagebutton'] = array( + 'arguments' => array('element'), + ); + $theme['panels_edit_display_form'] = array( + 'arguments' => array('form'), + 'file' => 'includes/display-edit.inc', + ); + $theme['panels_edit_layout_form_choose'] = array( + 'arguments' => array('form'), + 'file' => 'includes/display-edit.inc', + ); +/* $theme['panels_edit_layout_form_change'] = array( + 'arguments' => array('form'), + 'file' => 'includes/display-edit.inc', + );*/ + $theme['panels_pane'] = array( + 'arguments' => array('content', 'pane', 'display'), + 'file' => 'includes/render.inc', + ); + $theme['panels_common_content_list'] = array( + 'arguments' => array('display'), + 'file' => 'includes/common.inc', + ); + $theme['panels_common_context_list'] = array( + 'arguments' => array('object'), + 'file' => 'includes/common.inc', + ); + $theme['panels_common_context_item_form'] = array( + 'arguments' => array('form'), + 'file' => 'includes/common.inc', + ); + $theme['panels_common_content_item_row'] = array( + 'arguments' => array('type', 'form', 'position', 'count', 'with_tr' => TRUE), + 'file' => 'includes/common.inc', + ); + $theme['panels_dnd'] = array( + 'arguments' => array('content'), + 'file' => 'includes/display-edit.inc', + 'function' => 'theme_panels_dnd', + ); + $theme['panels_panel_dnd'] = array( + 'arguments' => array('content', 'area', 'label', 'footer'), + 'file' => 'includes/display-edit.inc', + 'function' => 'theme_panels_panel_dnd', + ); + $theme['panels_pane_dnd'] = array( + 'arguments' => array('block', 'id', 'label', 'left_buttons' => NULL, 'buttons' => NULL), + 'file' => 'includes/display-edit.inc', + ); + $theme['panels_pane_collapsible'] = array( + 'arguments' => array('block'), + 'file' => 'includes/display-edit.inc', + ); + + // Register layout and style themes on behalf of all of these items. + panels_load_include('plugins'); + + // No need to worry about files; the plugin has to already be loaded for us + // to even know what the theme function is, so files will be auto included. + $layouts = panels_get_layouts(); + foreach ($layouts as $name => $data) { + if (!empty($data['theme'])) { + $theme[$data['theme']] = array( + 'arguments' => array('id' => NULL, 'content' => NULL, 'settings' => NULL), + ); + } } + + $styles = panels_get_styles(); + foreach ($styles as $name => $data) { + if (!empty($data['render pane'])) { + $theme[$data['render pane']] = array( + 'arguments' => array('content' => NULL, 'pane' => NULL, 'display' => NULL), + ); + } + if (!empty($data['render panel'])) { + $theme[$data['render panel']] = array( + 'arguments' => array('display' => NULL, 'panel_id' => NULL, 'panes' => NULL, 'settings' => NULL), + ); + } + + } + + return $theme; } /** - * Implementation of hook_perm() + * Implementation of hook_menu */ -function panels_perm() { - return array('create panels'); +function panels_menu() { + $items = array(); + + // Provide some common options to reduce code repetition. + // By using array addition and making sure these are the rightmost + // value, they won't override anything already set. + $base = array( + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + 'file' => 'includes/display-edit.inc', + ); + + $items['panels/ajax/add-pane'] = array( + 'page callback' => 'panels_ajax_add_pane_choose', + ) + $base; + $items['panels/ajax/add-pane-config'] = array( + 'page callback' => 'panels_ajax_add_pane_config', + ) + $base; + $items['panels/ajax/configure'] = array( + 'page callback' => 'panels_ajax_configure_pane', + ) + $base; + $items['panels/ajax/show'] = array( + 'page callback' => 'panels_ajax_toggle_shown', + 'page arguments' => array('show'), + ) + $base; + $items['panels/ajax/hide'] = array( + 'page callback' => 'panels_ajax_toggle_shown', + 'page arguments' => array('hide'), + ) + $base; + $items['panels/ajax/cache-method'] = array( + 'page callback' => 'panels_ajax_cache_method', + ) + $base; + $items['panels/ajax/cache-settings'] = array( + 'page callback' => 'panels_ajax_cache_settings', + ) + $base; + + // For panel settings on the edit layout settings page + $items['panels/ajax/style-settings'] = array( + 'page callback' => 'panels_ajax_style_settings', + 'file' => 'includes/display-layout-settings.inc', + ) + $base; + + // Non-display editor callbacks + $items['panels/node/autocomplete'] = array( + 'title' => t('Autocomplete node'), + 'page callback' => 'panels_node_autocomplete', + 'file' => 'includes/callbacks.inc', + ) + $base; + + // On behalf of the 'common' api in common.inc + $items['panels/common/ajax'] = array( + 'page callback' => 'panels_common_ajax', + 'file' => 'includes/common.inc', + ) + $base; + + // Provide a nice location for a panels admin panel. + // + // TODO: Provide a cute little panel-base administrative panel + // that provides things like "Recently edited", "recently added" + // and other administrative items to make dealing with the complexity + // of panels a little easier. + + $items['admin/panels'] = array( + 'title' => t('Panels'), + 'access arguments' => array('access administration pages'), + 'page callback' => 'system_admin_menu_block_page', + 'file' => 'system.admin.inc', + 'file path' => drupal_get_path('module', 'system'), + 'description' => t('Administer items related to the Panels module.'), + ); + + return $items; } /** - * Implementation of hook_menu() + * Implementation of hook_init() */ -function panels_menu($may_cache) { - if ($may_cache) { - $access = user_access('create panels'); - $items[] = array( - 'path' => 'admin/build/panels', - 'title' => t('Panels'), - 'access' => $access, - 'callback' => 'panels_list_page', - 'description' => t('Create pages on your site that are 2 or 3 columns'), - ); - $items[] = array( - 'path' => 'admin/build/panels/list', - 'title' => t('List'), - 'access' => $access, - 'callback' => 'panels_list_page', - 'weight' => -10, - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items[] = array( - 'path' => 'admin/build/panels/add', - 'title' => t('Add'), - 'access' => $access, - 'callback' => 'panels_add_page', - 'type' => MENU_LOCAL_TASK, - ); - $items[] = array( - 'path' => 'admin/build/panels/add/layout', - 'title' => t('Add'), - 'access' => $access, - 'callback' => 'panels_add_layout_page', - 'type' => MENU_LOCAL_TASK, - ); - $items[] = array( - 'path' => 'admin/build/panels/edit', - 'title' => t('Edit panels'), - 'access' => $access, - 'callback' => 'panels_edit_page', - 'type' => MENU_CALLBACK, - ); - $items[] = array( - 'path' => 'admin/build/panels/delete', - 'title' => t('Delete panels'), - 'access' => $access, - 'callback' => 'drupal_get_form', - 'callback arguments' => array('panels_delete_confirm'), - 'type' => MENU_CALLBACK, - ); - - $items[] = array( - 'path' => 'panels/node/autocomplete', - 'title' => t('Autocomplete node'), - 'callback' => 'panels_node_autocomplete', - 'access' => user_access('access content'), - 'type' => MENU_CALLBACK - ); - - // load panels from database - $result = db_query("SELECT * FROM {panels_info}"); - // FIXME: Fow now we're making these all callbacks, but we - // should steal code from Views so they can be normal, tabs, - // etc - while ($panels = db_fetch_object($result)) { - $items[] = array( - 'path' => $panels->path, - 'title' => $panels->title, - 'access' => panels_access(unserialize($panels->access)), - 'callback' => 'panels_panels_page', - 'callback arguments' => array($panels->did), - 'type' => MENU_CALLBACK - ); - } - } - return $items; +function panels_init() { + drupal_add_css(panels_get_path('css/panels.css')); + drupal_add_js(panels_get_path('js/panels.js')); } /** - * Determine whether or not the current user has access to this - * panels. + * Load a panels include file. */ -function panels_access($access) { - // for now - return TRUE; +function panels_load_include($include, $path = 'includes/') { + require_once './' . panels_get_path("$path$include.inc"); } /** * panels path helper function */ -function panels_get_file_path($module, $file, $base_path = true) { - if ($base_path) { - $output = base_path(); - } +function panels_get_path($file, $base_path = FALSE, $module = 'panels') { + $output = $base_path ? base_path() : ''; return $output . drupal_get_path('module', $module) . '/' . $file; } -// --------------------------------------------------------------------------- -// panels custom image button +/** + * Implementation of hook_perm + */ +function panels_perm() { + return array( + 'view all panes', + 'view pane admin links', + 'administer pane visibility', + 'administer pane access', + 'administer advanced pane settings', + 'use panels caching features' + ); +} /** - * Custom form element to do our nice images. + * Get an object from cache. */ -function panels_elements() { - $type['panels_imagebutton'] = array('#input' => TRUE, '#button_type' => 'submit',); - return $type; +function panels_cache_get($obj, $did, $skip_cache = FALSE) { + static $cache = array(); + $key = "$obj:$did"; + if ($skip_cache) { + unset($cache[$key]); + } + + if (!array_key_exists($key, $cache)) { + $data = db_fetch_object(db_query("SELECT * FROM {panels_object_cache} WHERE sid = '%s' AND obj = '%s' AND did = %d", session_id(), $obj, $did)); + if ($data) { + $cache[$key] = unserialize($data->data); + } + } + return isset($cache[$key]) ? $cache[$key] : NULL; } /** - * Theme our image button. + * Save the edited object into the cache. */ -function theme_panels_imagebutton($element) { - return '\n"; +function panels_cache_set($obj, $did, $cache) { + panels_cache_clear($obj, $did); + db_query("INSERT INTO {panels_object_cache} (sid, obj, did, data, timestamp) VALUES ('%s', '%s', %d, '%s', %d)", session_id(), $obj, $did, serialize($cache), time()); } -function panels_imagebutton_value() { - // null function guarantees default_value doesn't get moved to #value. +/** + * Clear a object from the cache; used if the editing is aborted. + */ +function panels_cache_clear($obj, $did) { + db_query("DELETE FROM {panels_object_cache} WHERE sid = '%s' AND obj = '%s' AND did = %d", session_id(), $obj, $did); } /** - * Add a single button to a form. + * Implementation of hook_cron. Clean up old caches. */ -function panels_add_button($image, $name, $text) { - $module_path = base_path() . drupal_get_path('module', 'panels'); +function panels_cron() { + // delete anything 7 days old or more. + db_query("DELETE FROM {panels_object_cache} WHERE timestamp < %d", time() - (86400 * 7)); +} - return array( - '#type' => 'panels_imagebutton', - '#image' => $module_path . '/images/' . $image, - '#title' => $text, - '#default_value' => $name, - ); +/** + * Global storage function, used mostly so that _submit hooks can pass data + * back to their originator more easily. TODO: deprecated but still in use. + */ +function panels_set($var, $value = NULL) { + static $vars = array(); + if ($value !== NULL) { + $vars[$var] = $value; + } + return $vars[$var]; } /** - * Set a button to a blank image -- used for placeholders when buttons are - * not relevant but just removing it would be visually unappealing. + * Retrieve from global storage */ -function panels_set_blank(&$form) { - $form['#type'] = 'markup'; - $form['#value'] = theme('image', drupal_get_path('module', 'panels') . '/images/blank.gif'); +function panels_get($var) { + return panels_set($var); } // --------------------------------------------------------------------------- -// panels administrative pages +// panels display editing /** - * Provide a list of panels, with links to edit or delete them. + * @defgroup mainapi Functions comprising the main panels API + * @{ */ -function panels_list_page() { - $result = db_query("SELECT * FROM {panels_info} ORDER BY title"); - while ($panels = db_fetch_object($result)) { - $item = array(); - $item[] = check_plain($panels->title); - $item[] = l($panels->path, $panels->path); - $item[] = implode(' | ', array( - l(t('Edit'), "admin/build/panels/edit/$panels->did"), - l(t('Delete'), "admin/build/panels/delete/$panels->did"), - )); - $items[] = $item; - } - $header = array( - t('Panel title'), - t('URL'), - t('Operations'), - ); - $output = theme('table', $header, $items); - return $output; -} -/* - * Provide a form to confirm deletion of a panel page. +/** + * Main API entry point to edit a panel display. + * + * Sample implementations utiltizing the the complex $destination behavior can be found + * in panels_page_edit_content() and, in a separate contrib module, OG Blueprints + * (http://drupal.org/project/og_blueprints), og_blueprints_blueprint_edit(). + * + * @ingroup mainapi + * + * @param object $display instanceof panels_display \n + * A fully loaded panels $display object, as returned from panels_load_display(). + * Merely passing a did is NOT sufficient. \n + * Note that 'fully loaded' means the $display must already be loaded with any contexts + * the caller wishes to have set for the display. + * @param mixed $destination \n + * The redirect destination that the user should be taken to on form submission or + * cancellation. With panels_edit, $destination has complex effects on the return + * values of panels_edit() once the form has been submitted. See the explanation of + * the return value below to understand the different types of values returned by panels_edit() + * at different stages of FAPI. Under most circumstances, simply passing in + * drupal_get_destination() is all that's necessary. + * @param array $content_types \n + * An associative array of allowed content types, typically as returned from + * panels_common_get_allowed_types(). Note that context partially governs available content types, + * so you will want to create any relevant contexts using panels_create_context() or + * panels_create_context_empty() to make sure all the appropriate content types are available. + * + * @return + * Because the functions called by panels_edit() invoke the form API, this function + * returns different values depending on the stage of form submission we're at. In Drupal 5, + * the phase of form submission is indicated by the contents of $_POST['op']. Here's what you'll + * get at different stages: + * -# If !$_POST['op']: then we're on on the initial passthrough and the form is being + * rendered, so it's the $form itself that's being returned. Because negative margins, + * a common CSS technique, bork the display editor's ajax drag-and-drop, it's important + * that the $output be printed, not returned. Use this syntax in the caller function: \n + * print theme('page', panels_edit($display, $destination, $content_types), FALSE); \n + * -# If $_POST['op'] == t('Cancel'): form submission has been cancelled. If empty($destination) == FALSE, + * then there is no return value and the panels API takes care of redirecting to $destination. + * If empty($destination) == TRUE, then there's still no return value, but the caller function + * has to take care of form redirection. + * -# If $_POST['op'] == ('Save'): the form has been submitted successfully and has run through + * panels_edit_display_submit(). $output depends on the value of $destination: + * - If empty($destination) == TRUE: $output contains the modified $display + * object, and no redirection will occur. This option is useful if the caller + * needs to perform additional operations on or with the modified $display before + * the page request is complete. Using hook_form_alter() to add an additional submit + * handler is typically the preferred method for something like this, but there + * are certain use cases where that is infeasible and $destination = NULL should + * be used instead. If this method is employed, the caller will need to handle form + * redirection. Note that having $_REQUEST['destination'] set, whether via + * drupal_get_destination() or some other method, will NOT interfere with this + * functionality; consequently, you can use drupal_get_destination() to safely store + * your desired redirect in the caller function, then simply use drupal_goto() once + * panels_edit() has done its business. + * - If empty($destination) == FALSE: the form will redirect to the URL string + * given in $destination and NO value will be returned. */ -function panels_delete_confirm($did = '') { - $panels = panels_load_panels($did); - - if (!$panels) { - drupal_goto('admin/build/panels'); - } +function panels_edit($display, $destination = NULL, $content_types = NULL) { + panels_load_include('display-edit'); + panels_load_include('ajax'); + panels_load_include('plugins'); + return _panels_edit($display, $destination, $content_types); +} - $form['did'] = array('#type' => 'value', '#value' => $panels->did); - return confirm_form( $form, - t('Are you sure you want to delete %title?', array('%title' => $panels->title)), - $_GET['destination'] ? $_GET['destination'] : 'admin/build/panels', - t('This action cannot be undone.'), - t('Delete'), t('Cancel') - ); +/** + * API entry point for selecting a layout for a given display. + * + * Layout selection is nothing more than a list of radio items encompassing the available + * layouts for this display, as defined by .inc files in the panels/layouts subdirectory. + * The only real complexity occurs when a user attempts to change the layout of a display + * that has some content in it. + * + * @param object $display instanceof panels_display \n + * A fully loaded panels $display object, as returned from panels_load_display(). + * Merely passing a did is NOT sufficient. + * @param string $finish + * A string that will be used for the text of the form submission button. If no value is provided, + * then the form submission button will default to t('Save'). + * @param mixed $destination + * Basic usage is a string containing the URL that the form should redirect to upon submission. + * For a discussion of advanced usages, see panels_edit(). + * @param mixed $allowed_layouts + * Allowed layouts has three different behaviors that depend on which of three value types + * are passed in by the caller: + * #- if $allowed_layouts instanceof panels_allowed_layouts (includes subclasses): the most + * complex use of the API. The caller is passing in a loaded panels_allowed_layouts object + * that the client module previously created and stored somewhere using a custom storage + * mechanism. + * #- if is_string($allowed_layouts): the string will be used in a call to variable_get() which + * will call the $allowed_layouts . '_allowed_layouts' var. If the data was stored properly + * in the system var, the $allowed_layouts object will be unserialized and recreated. + * @see panels_common_set_allowed_layouts() + * #- if is_null($allowed_layouts): the default behavior, which also provides backwards + * compatibility for implementations of the Panels2 API written before beta4. In this case, + * a dummy panels_allowed_layouts object is created which does not restrict any layouts. + * Subsequent behavior is indistinguishable from pre-beta4 behavior. + * + * @return + * Can return nothing, or a modified $display object, or a redirection string; return values for the + * panels_edit* family of functions are quite complex. See panels_edit() for detailed discussion. + * @see panels_edit() + */ +function panels_edit_layout($display, $finish, $destination = NULL, $allowed_layouts = NULL) { + panels_load_include('display-layout'); + panels_load_include('plugins'); + return _panels_edit_layout($display, $finish, $destination, $allowed_layouts); } -/* - * Handle the submit button to delete a panel page. +/** + * API entry point for configuring the layout settings for a given display. + * + * For all layouts except Flexible, the layout settings form allows the user to select styles, + * as defined by .inc files in the panels/styles subdirectory, for the panels in their display. + * For the Flexible layout, the layout settings form allows the user to provide dimensions + * for their flexible layout in addition to applying styles to panels. + * + * @param object $display instanceof panels_display \n + * A fully loaded panels $display object, as returned from panels_load_display(). + * Merely passing a did is NOT sufficient. + * @param string $finish + * A string that will be used for the text of (one of) the form submission button(s). Note that + * panels will NOT wrap $finish in t() for you, so your caller should make sure to do so. \n + * The submit behavior of the form is primarily governed by the value of $destination (see + * below), but is secondarily governed by $finish as follows: + * -# If $finish != t('Save'), then two #submit buttons will be present: one with the button + * text t('Save'), and the other with the button text $finish. . + * - Clicking the 'Save' button will save any changes on the form to the $display object and + * keep the user on the same editing page. + * - Clicking the $finish button will also save the $display object, but the user will be + * redirected to the URL specified in $destination. + * -# If $finish == t('Save'), then there is only one button, still called t('Save'), but it + * mimics the behavior of the $finish button above by redirecting the user away from the form. + * @param mixed $destination + * Basic usage is a string containing the URL that the form should redirect to upon submission. + * For a discussion of advanced usages that rely on NULL values for $destination, see the + * panels_edit() documentation. + * @param mixed $title + * The $title variable has three modes of operation: + * -# If $title == FALSE (the default), then no widget will appear on the panels_edit_layout_settings form + * allowing the user to select a title, and other means for setting page titles will take precedent. If + * no other means are used to provide a title, then the title will be hidden when rendering the $display. + * -# If $title == TRUE, then two widgets will appear on the panels_edit_layout_settings form allowing the + * user to input a title specific to this $display, as well as a checkbox enabling the user to disable + * page titles entirely for this $display object. + * -# If $title == (string), then the behavior is very similar to mode 2, but the widget description + * on the title textfield will indicate that the $title string will be used as the default page title + * if none is provided on this form. When utilizing this option, note that the panels API can only + * provide the data for these values; you must implement the appropriate conditionals to make it true. + * + * @return + * Can return nothing, or a modified $display object, or a redirection string; return values for the + * panels_edit* family of functions are quite complex. See panels_edit() for detailed discussion. + * @see panels_edit() */ -function panels_delete_confirm_submit($formid, $form) { - if ($form['confirm']) { - panels_delete_panels((object) $form); - drupal_goto('admin/build/panels'); - } +function panels_edit_layout_settings($display, $finish, $destination = NULL, $title = FALSE) { + panels_load_include('display-layout-settings'); + panels_load_include('ajax'); + panels_load_include('plugins'); + return _panels_edit_layout_settings($display, $finish, $destination, $title); } + +// --------------------------------------------------------------------------- +// panels database functions + /** - * Handle the add panels page + * Forms the basis of a panel display + * */ -function panels_add_page($layout = NULL) { - $layouts = panels_get_layouts(); - drupal_add_css(drupal_get_path('module', 'panels') . '/panels_admin.css'); - if (!$layout) { - foreach ($layouts as $id => $layout) { - if (!$default_id) { - // grab the first one for our default. - $default_id = $id; +class panels_display { + var $args = array(); + var $content = array(); + var $panels = array(); + var $incoming_content = NULL; + var $css_id = NULL; + var $context = array(); + var $layout_settings = array(); + var $panel_settings = array(); + var $cache = array(); + var $title = ''; + var $hide_title = 0; + + function add_pane($pane, $location = FALSE) { + $pane->pid = $this->next_new_pid(); + if (!$location || !isset($this->panels[$location])) { + foreach ($this->panels as $panel_name => $panel) { + if (array_key_exists($pane->pid, $panel)) { + $this->panels[$panel_name][] = $pane->pid; + } } - $file = panels_get_file_path($layout['module'], $layout['icon'], false); - $output .= theme('panels_add_image', $layout[title], $id, l(theme('image', $file), $_GET['q'] . '/' . $id, NULL, NULL, NULL, NULL, TRUE)); } - return $output; + else { + $this->panels[$location][] = $pane->pid; + } } - if (!$layouts[$layout]) { - return drupal_not_found(); + function duplicate_pane($pid, $location = FALSE) { + $pane = $this->clone_pane($pid); + $this->add_pane($pane, $location); } - $panels->layout = $layout; - return drupal_get_form('panels_edit_form', $panels); -} - -function theme_panels_add_image($title, $id, $image) { - $output .= ''; - return $output; -} -// --------------------------------------------------------------------------- -// panels administrative pages + function clone_pane($pid) { + $pane = drupal_clone($this->content[$pid]); + foreach (array_keys($this->content) as $pidcheck) { + // necessary? + unset($pane->position); + } + return $pane; + } -function panels_edit_page($did = NULL) { - if (!$did || !($panels = panels_load_panels($did))) { - return drupal_not_found(); + function next_new_pid() { + // necessary if/until we use this method and ONLY this method for adding temporary pids. + // then we can do it with a nice static var. + $id = array(0); + foreach (array_keys($this->content) as $pid) { + if (!is_numeric($pid)) { + $id[] = substr($pid, 4); + } + } + $next_id = end($id); + return ++$next_id; } - return drupal_get_form('panels_edit_form', $panels); } /** - * shortcut to ease the syntax of the various form builder tricks we use. + * }@ End of 'defgroup mainapi', although other functions are specifically added later */ -function panels_form_builder(&$form, $form_id = 'panels_edit_form') { - $form['#post'] = $_POST; - $form = form_builder($form_id, $form); + +function panels_export_pane_across_displays($source_display, &$target_display, $pid, $location = FALSE) { + $pane = $source_display->clone_pane($pid); + $target_display->add_pane($pane, $location); } /** - * Edit an already loaded panels. + * Clean up a display object and add some required information, if missing. + * + * Currently a display object needs 'args', 'incoming content', 'context' + * and a 'css_id'. + * + * @param &$display + * The display object to be sanitized. + * @return + * The sanitized display object. */ -function panels_edit_form($panels) { - drupal_add_css(drupal_get_path('module', 'panels') . '/panels_admin.css'); - $layouts = panels_get_layouts(); - $layout = $layouts[$panels->layout]; - - $content_types = panels_get_content_types(); +function panels_sanitize_display(&$display) { + if (!isset($display->args)) { + $display->args = array(); + } - // Process all our add button stuff first so we can add stuff to the - // form semi dynamically. + if (!isset($display->incoming_content)) { + $display->incoming_content = NULL; + } - $form['add'] = array( - '#type' => 'fieldset', - '#title' => t('Add content'), - '#collapsible' => false, - '#description' => t('Select an area to add content to, then select a type of content and click the appropriate button. The content will be added.'), - ); + if (!isset($display->context)) { + $display->context = array(); + } - $default_radio = array_shift(array_keys($layout['content areas'])); - $form['add']['area'] = array( - '#type' => 'radios', - '#title' => t('Area'), - '#options' => $layout['content areas'], - '#prefix' => '
', - '#suffix' => '
', - '#default_value' => $default_radio, - ); - foreach ($content_types as $id => $type) { - $function = $type['admin']; - if (function_exists($function)) { - global $form_values; - $form['add'][$id] = $function('add button', $dummy); - - // $dummy needed for cause you can't have default args on a reference. - $form['add'][$id]['#parents'] = array('add', $id); - $form['add'][$id]['#tree'] = true; - panels_form_builder($form['add'][$id]); - - if ($conf = $function('add', $form_values['add'][$id])) { - $add->configuration = $conf; - $add->type = $id; - $form['add']['area']['#parents'] = array('area'); - panels_form_builder($form['add']['area']); - $add->area = $form_values['area']; - } - } + if (!isset($display->css_id)) { + $display->css_id = NULL; } +} - $form['layout'] = array( - '#type' => 'value', - '#value' => $panels->layout - ); +/** + * Creates a new display, setting the ID to our magic new id. + */ +function panels_new_display() { + $display = new panels_display(); + $display->did = 'new'; + return $display; +} - $form['did'] = array( - '#type' => 'value', - '#value' => $panels->did, - ); +function panels_new_pane($type, $subtype) { + $pane = new stdClass(); + $pane->pid = 'new'; + $pane->type = $type; + $pane->subtype = $subtype; + $pane->configuration = array(); + $pane->access = array(); + $pane->shown = TRUE; + return $pane; +} - $form['info'] = array( - '#type' => 'fieldset', - '#title' => t('General information'), - '#collapsible' => false, - ); +/** + * Load and fill the requested $display object(s). + * + * Helper function primarily for for panels_load_display(). + * + * @param array $dids + * An indexed array of dids to be loaded from the database. + * + * @return $displays + * An array of displays, keyed by their display dids. + */ +function panels_load_displays($dids) { + $displays = array(); + if (empty($dids) || !is_array($dids)) { + return $displays; + } - $file = panels_get_file_path($layout['module'], $layout['icon'], false); - $icon .= theme('image', $file); - $form['info']['layout-icon'] = array( - '#value' => '
' . $icon . '
', - ); + $subs = implode(', ', array_fill(0, count($dids), '%d')); - $form['info']['layout-display'] = array( - '#value' => 'Layout: ' . $layout['title'], - ); + $result = db_query("SELECT * FROM {panels_display} WHERE did IN ($subs)", $dids); - $form['info']['title'] = array( - '#type' => 'textfield', - '#default_value' => $panels->title, - '#title' => t('Page title'), - '#description' => t('The page title for this panels layout'), - ); + while ($obj = db_fetch_array($result)) { + $display = new panels_display(); - $form['info']['css_id'] = array( - '#type' => 'textfield', - '#default_value' => $panels->css_id, - '#title' => t('CSS ID'), - '#description' => t('The CSS ID to apply to this page'), - ); - - $form['info']['path'] = array( - '#type' => 'textfield', - '#default_value' => $panels->path, - '#title' => t('Path'), - '#description' => t('The URL path to give this page, i.e, path/to/page'), - '#required' => TRUE, - ); - - $form['content'] = array( - '#tree' => true, - ); - - // Go through our content areas and display what we have. - foreach ($layout['content areas'] as $area => $title) { - $list = array(); - $form['content'][$area] = array( - '#tree' => true, - ); - - // Construct the order, feeding it the default order for what - // we know about. When we pull it back out, it may well be - // different due to past submits. - $order = array(); - if (is_array($panels->content[$area])) { - $order = array_keys($panels->content[$area]); - } - $form['content'][$area]['order'] = array( - '#type' => 'hidden', - '#default_value' => serialize($order), - '#parents' => array('content', $area, 'order'), - ); - - // If an add button has added an item to the area, put it in and update - // the $order. - panels_form_builder($form['content'][$area]['order']); - $order = unserialize($form['content'][$area]['order']['#value']); - if ($add->area == $area) { - // say THIS 5 times real fast - if ($panels->content[$area]) { - $position = max(max(array_keys($order)), max(array_keys($panels->content[$area]))) + 1; - } - else if ($order) { - $position = max(array_keys($order)) + 1; - } - else { - $position = 0; + foreach ($obj as $key => $value) { + $display->$key = $value; + // unserialize important bits + if (in_array($key, array('layout_settings', 'panel_settings', 'cache'))) { + $display->$key = empty($display->$key) ? array() : unserialize($display->$key); } - - $panels->content[$area][$position] = $add; - $order[] = $position; - $form['content'][$area]['order']['#value'] = serialize($order); } - // Go through each item in the area and render it. - $count = count($order); - foreach ($order as $position => $id) { - // place buttons to re-order content. - $form['content'][$area][$id]['buttons'] = array( - '#parents' => array('content', $area, $id, 'buttons'), - '#tree' => TRUE - ); - panels_add_buttons($form['content'][$area][$id]['buttons'], $count, $position); - - // figure out if one of those buttons was pressed - panels_form_builder($form['content'][$area][$id]['buttons']); - $deleted = false; - foreach ($GLOBALS['form_values']['content'][$area][$id]['buttons'] as $button => $value) { - if ($value) { - $function = 'panels_move_' . $button; - $function($order, $position); - $form['content'][$area]['order']['#value'] = serialize($order); - if ($button == 'delete') - $deleted = true; - } - } - // If a content item was deleted, it still has buttons. Get rid of them. - // It had buttons because we needed to give it buttons to see if its - // buttons were clicked. - if ($deleted) { - unset($form['content'][$area][$id]['buttons']); - } - else { - // we finally get to add the conent item's content to the form. - $area_record = $panels->content[$area][$id]; - $form['content'][$area][$id]['type'] = array( - '#type' => 'hidden', - '#default_value' => $area_record->type, - '#parents' => array('content', $area, $id, 'type'), - ); - // retrieve what was already there -- so we can retain edits and - // content items that were added. - panels_form_builder($form['content'][$area][$id]['type']); - $type = $form['content'][$area][$id]['type']['#value']; - $function = $content_types[$type]['admin']; - if (function_exists($function)) { - $array = array( - '#tree' => true, - '#parents' => array('content', $area, $id, 'configuration'), - ); - $form['content'][$area][$id]['configuration'] = array_merge($array, $function('edit', $area_record->configuration, array('content', $area, $id, 'configuration'))); - panels_form_builder($form['content'][$area][$id]['configuration']); - } - } - } + $display->panels = $display->content = array(); + + $displays[$display->did] = $display; + } + + foreach (module_implements('panels_layout_content_alter') as $module) { + $function = $module . '_panels_layout_content_alter'; + $function($content, $layout, $settings); } - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); + $result = db_query("SELECT * FROM {panels_pane} WHERE did IN ($subs) ORDER BY did, panel, position", $dids); - return $form; + while ($pane = db_fetch_object($result)) { + $pane->configuration = unserialize($pane->configuration); + $pane->cache = empty($pane->cache) ? array() : unserialize($pane->cache); + $pane->access = ($pane->access ? explode(', ', $pane->access) : array()); + // Old panels may not have shown property, so enable by default when loading. + $pane->shown = isset($pane->shown) ? $pane->shown : TRUE; + + $displays[$pane->did]->panels[$pane->panel][] = $pane->pid; + $displays[$pane->did]->content[$pane->pid] = $pane; + } + return $displays; } /** - * Display the form to edit a panels. + * Load a single display. + * + * @ingroup mainapi + * + * @param int $did + * The display id (did) of the display to be loaded. + * + * @return object $display instanceof panels_display \n + * Returns a partially-loaded panels_display object. $display objects returned from + * from this function have only the following data: + * - $display->did (the display id) + * - $display->name (the 'name' of the display, where applicable - it often isn't) + * - $display->layout (a string with the system name of the display's layout) + * - $display->panel_settings (custom layout style settings contained in an associative array; NULL if none) + * - $display->layout_settings (panel size and configuration settings for Flexible layouts; NULL if none) + * - $display->css_id (the special css_id that has been assigned to this display, if any; NULL if none) + * - $display->content (an array of pane objects, keyed by pane id (pid)) + * - $display->panels (an associative array of panel regions, each an indexed array of pids in the order they appear in that region) + * - $display->cache (any relevant data from panels_simple_cache) + * - $display->args + * - $display->incoming_content + * + * While all of these members are defined, $display->context is NEVER defined in the returned $display; + * it must be set using one of the panels_context_create() functions. */ -function theme_panels_edit_form($form) { - $layouts = panels_get_layouts(); - $layout = $layouts[$form['layout']['#value']]; - - $content_types = panels_get_content_types(); +function panels_load_display($did) { + $displays = panels_load_displays(array($did)); + if (!empty($displays)) { + return array_shift($displays); + } +} - $output .= drupal_render($form['info']); - foreach ($layout['content areas'] as $area => $title) { - $order = unserialize($form['content'][$area]['order']['#value']); - if (!$order) { - $area_content = t('This area has no content.'); - } - else { - $area_content = ''; - $count = count($order); - foreach ($order as $position => $id) { - if ($count > 1) { - if ($position == 0 ) { - panels_set_blank($form['content'][$area][$id]['buttons']['up']); - } - else if ($position == ($count - 1)) { - panels_set_blank($form['content'][$area][$id]['buttons']['down']); - } - } - $type = $form['content'][$area][$id]['type']['#value']; - $function = $content_types[$type]['admin']; - if (function_exists($function)) { - // figure out the actual values; using the global because we need it - // to be in the same format it'll be in 'submit'. - global $form_values; - $conf_form = $form_values['content'][$area][$id]['configuration']; - $conf = $function('save', $conf_form); - $fieldset = array( - '#title' => t('Configure'), - '#children' => drupal_render($form['content'][$area][$id]['configuration']), - '#collapsible' => true, - '#collapsed' => true - ); - $buttons = drupal_render($form['content'][$area][$id]['buttons']); - $area_content .= $buttons . ' ' . $function('list', $conf) . - theme('fieldset', $fieldset) /* . '
' */; - } - } +/** + * Save a display object. + * + * @ingroup mainapi + * + * Note a new $display only receives a real did once it is run through this function. + * Until then, it uses a string placeholder, 'new', in place of a real did. The same + * applies to all new panes (whether on a new $display or not); in addition, + * panes have sequential numbers appended, of the form 'new-1', 'new-2', etc. + * + * @param object $display instanceof panels_display \n + * The display object to be saved. Passed by reference so the caller need not use + * the return value for any reason except convenience. + * + * @return object $display instanceof panels_display \n + */ +function panels_save_display(&$display) { + // @todo -- update all this to just use drupal_write_record or something like it. + if (!empty($display->did) && $display->did != 'new') { + db_query("UPDATE {panels_display} SET layout = '%s', layout_settings = '%s', panel_settings = '%s', cache = '%s', title = '%s', hide_title = %d WHERE did = %d", $display->layout, serialize($display->layout_settings), serialize($display->panel_settings), serialize($display->cache), $display->title, $display->hide_title, $display->did); + // Get a list of all panes currently in the database for this display so we can know if there + // are panes that need to be deleted. (i.e, aren't currently in our list of panes). + $result = db_query("SELECT pid FROM {panels_pane} WHERE did = %d", $display->did); + while ($pane = db_fetch_object($result)) { + $pids[$pane->pid] = $pane->pid; } - $content[$area] = theme('fieldset', array('#title' => check_plain($title), '#value' => $area_content)); + } + else { + db_query("INSERT INTO {panels_display} (layout, layout_settings, panel_settings, cache, title, hide_title) VALUES ('%s', '%s', '%s', '%s', '%s', %d)", $display->layout, serialize($display->layout_settings), serialize($display->panel_settings), serialize($display->cache), $display->title, $display->hide_title); + $display->did = db_last_insert_id('panels_display', 'did'); + $pids = array(); } - $output .= panels_get_layout($layout, $content); + // update all the panes + panels_load_include('plugins'); - $output .= drupal_render($form); - return $output; -} + foreach ((array) $display->panels as $id => $panes) { + $position = 0; + $new_panes = array(); + foreach ((array) $panes as $pid) { + $pane = $display->content[$pid]; + $pane->position = $position++; -function panels_edit_form_validate($form_id, $form_values, $form) { - $content_types = panels_get_content_types(); - foreach ($form_values['content'] as $area => $content) { - foreach ($content as $id => $item) { - if (is_numeric($id)) { - $function = $content_types[$item['type']]['admin']; - if (function_exists($function)) { - $function('validate', $item['configuration'], $form['content'][$area][$id]['configuration']); - } + // make variables right. + $type = panels_get_content_type($pane->type); + $access = isset($pane->access) ? implode(', ', $pane->access) : ''; + $visibility = $type['visibility serialize'] ? serialize($pane->visibility) : $pane->visibility; + $pane->shown = isset($pane->shown) ? $pane->shown : TRUE; + + if (empty($pane->cache)) { + $pane->cache = array(); } - } - } -} -function panels_edit_form_submit($form_id, $form_values) { - $panels = (object) $form_values; - // be sure we get the order right. - foreach ($form_values['content'] as $area => $content) { - $array = array(); - $order = unserialize($content['order']); - if (is_array($order)) { - foreach($order as $id) { - $array[] = $content[$id]; + $v = array($display->did, $pane->panel, $pane->type, $pane->subtype, serialize($pane->configuration), serialize($pane->cache), $pane->shown, $access, $visibility, $pane->position); + + if (!is_numeric($pid)) { + unset($display->content[$pid]); + // doin it this way for readability + $f = 'did, panel, type, subtype, configuration, cache, shown, access, visibility, position'; + $q = "%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d"; + + db_query("INSERT INTO {panels_pane} ($f) VALUES ($q)", $v); + $pane->pid = db_last_insert_id('panels_pane', 'pid'); + } + else { + $v[] = $pane->pid; + $f = "did = %d, panel = '%s', type = '%s', subtype = '%s', configuration = '%s', cache = '%s', shown = '%s', access = '%s', visibility = '%s', position = '%d'"; + db_query("UPDATE {panels_pane} SET $f WHERE pid = %d", $v); + } + // and put it back so our pids and positions can be used + $display->content[$pane->pid] = $pane; + $new_panes[] = $pane->pid; + if (isset($pids[$pane->pid])) { + unset($pids[$pane->pid]); } } - $panels->content[$area] = $array; - } - panels_save_panels($panels); - drupal_set_message(t('The panels has been saved.')); - return 'admin/build/panels'; -} -/** - * add the buttons to a content item - */ -function panels_add_buttons(&$form, $count, $position) { - $form['delete'] = panels_add_button('user-trash.png', 'delete', t('Delete this item')); - // Leaving these in but commented out as I'm not convinced we don't want them. -// if ($count > 2) { -// $form['top'] = panels_add_button('go-top.png', 'top', t('Move item to top')); -// } - if ($count > 1) { - $form['up'] = panels_add_button('go-up.png', 'up', t('Move item up')); - $form['down'] = panels_add_button('go-down.png', 'down', t('Move item down')); + $display->panels[$id] = $new_panes; + } + if ($pids) { + db_query("DELETE FROM {panels_pane} WHERE pid IN (" . db_placeholders($pids) . ")", $pids); } -// if ($count > 2) { -// $form['bottom'] = panels_add_button('go-bottom.png', 'bottom', t('Move item to bottom')); -// } -// if ($count > 1) { -// $form['spacer'] = panels_add_blank(); -// } - return $form; -} - -/** - * move an item in an array to the top - */ -function panels_move_top(&$array, &$position) { - $value = $array[$position]; - unset($array[$position]); - array_unshift($array, $value); - // reindex the array now - $array = array_values($array); -} -/** - * move an item in an array to the bottom - */ -function panels_move_bottom(&$array, &$position) { - $value = $array[$position]; - unset($array[$position]); - $array[] = $value; - // reindex the array now - $array = array_values($array); -} + // Clear any cached content for this display. + panels_clear_cached_content($display); -/** - * move an item in an array up one position - */ -function panels_move_up(&$array, &$position) { - $value = $array[$position]; - $array[$position] = $array[$position - 1]; - $array[$position - 1] = $value; + // to be nice, even tho we have a reference. + return $display; } /** - * move an item in an array up one position + * Delete a display. */ -function panels_move_down(&$array, &$position) { - $value = $array[$position]; - $array[$position] = $array[$position + 1]; - $array[$position + 1] = $value; +function panels_delete_display($display) { + if (is_object($display)) { + $did = $display->did; + } + else { + $did = $display; + } + db_query("DELETE FROM {panels_display} WHERE did = %d", $did); + db_query("DELETE FROM {panels_pane} WHERE did = %d", $did); } /** - * Remove an item from an array + * Exports the provided display into portable code. + * + * This function is primarily intended as a mechanism for cloning displays. + * It generates an exact replica (in code) of the provided $display, with + * the exception that it replaces all ids (dids and pids) with 'new-*' values. + * Only once panels_save_display() is called on the code version of $display will + * the exported display written to the database and permanently saved. + * + * @see panels_page_export() or _panels_page_fetch_display() for sample implementations. + * + * @ingroup mainapi + * + * @param object $display instanceof panels_display \n + * This export function does no loading of additional data about the provided + * display. Consequently, the caller should make sure that all the desired data + * has been loaded into the $display before calling this function. + * @param string $prefix + * A string prefix that is prepended to each line of exported code. This is primarily + * used for prepending a double space when exporting so that the code indents and lines up nicely. + * + * @return string $output + * The passed-in $display expressed as code, ready to be imported. Import by running + * eval($output) in the caller function; doing so will create a new $display variable + * with all the exported values. Note that if you have already defined a $display variable in + * the same scope as where you eval(), your existing $display variable WILL be overwritten. */ -function panels_move_delete(&$array, &$position) { - unset($array[$position]); - // reindex the array now - $array = array_values($array); -} - -// --------------------------------------------------------------------------- -// panels database functions - -function panels_load_panels($did) { - $panels = db_fetch_object(db_query("SELECT * FROM {panels_info} WHERE did = %d", $did)); - if (!$panels) { - return NULL; - } - $result = db_query("SELECT * FROM {panels_area} WHERE did = %d ORDER BY area, position", $did); - while ($area = db_fetch_object($result)) { - $area->configuration = unserialize($area->configuration); - $panels->content[$area->area][] = $area; +function panels_export_display($display, $prefix = '') { + $output = ''; + $output .= $prefix . '$display = new panels_display()' . ";\n"; + $output .= $prefix . '$display->did = \'new\'' . ";\n"; + $fields = array('name', 'layout', 'layout_settings', 'panel_settings'); + foreach ($fields as $field) { + $output .= $prefix . '$display->' . $field . ' = ' . panels_var_export($display->$field, $prefix) . ";\n"; } - return $panels; -} -function panels_save_panels($panels) { - if ($panels->did) { - db_query("UPDATE {panels_info} SET title = '%s', access = '%s', path = '%s', css_id = '%s', layout = '%s' WHERE did = %d", $panels->title, $panels->access, $panels->path, $panels->css_id, $panels->layout, $panels->did); - db_query("DELETE FROM {panels_area} WHERE did = %d", $panels->did); - } - else { - $panels->did = db_next_id("{panels_info_id}"); - db_query("INSERT INTO {panels_info} (did, title, access, path, css_id, layout) VALUES (%d, '%s', '%s', '%s', '%s', '%s')", $panels->did, $panels->title, $panels->access, $panels->path, $panels->css_id, $panels->layout); - } - foreach ($panels->content as $area => $info) { - foreach ($info as $position => $block) { - if (is_numeric($position)) { // don't save some random form stuff that may've been here. - $block = (object) $block; - db_query("INSERT INTO {panels_area} (did, area, type, configuration, position) VALUES(%d, '%s', '%s', '%s', %d)", $panels->did, $area, $block->type, serialize($block->configuration), $position); + $output .= $prefix . '$display->content = array()' . ";\n"; + $output .= $prefix . '$display->panels = array()' . ";\n"; + $panels = array(); + + if (!empty($display->content)) { + $pid_counter = 0; + $region_counters = array(); + foreach ($display->content as $pane) { + $pane->pid = 'new-' . ++$pid_counter; + $output .= panels_export_pane($pane, $prefix . ' '); + $output .= "$prefix " . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n"; + if (!isset($region_counters[$pane->panel])) { + $region_counters[$pane->panel] = 0; } + $output .= "$prefix " . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ .'] = \'' . $pane->pid . "';\n"; } } - menu_rebuild(); + return $output; } -function panels_delete_panels($panels) { - db_query("DELETE FROM {panels_info} WHERE did = %d", $panels->did); - db_query("DELETE FROM {panels_area} WHERE did = %d", $panels->did); - menu_rebuild(); +function panels_export_pane($pane, $prefix = '') { + $output = ''; + $output = $prefix . '$pane = new stdClass()' . ";\n"; + $fields = array('pid', 'panel', 'type', 'subtype', 'access', 'configuration'); + foreach ($fields as $field) { + $output .= "$prefix " . '$pane->' . $field . ' = ' . panels_var_export($pane->$field, "$prefix ") . ";\n"; + } + return $output; } -// --------------------------------------------------------------------------- -// panels page -function panels_panels_page($did) { - $panels = panels_load_panels($did); - if (!$panels) { - return drupal_not_found(); +function panels_var_export($object, $prefix = '') { + if (is_array($object) && empty($object)) { + $output = 'array()'; } - - $layouts = panels_get_layouts(); - $layout = $layouts[$panels->layout]; - $layout['css_id'] = $panels->css_id; - - if (!$layout) { - watchdog('panels', t('Unable to find requested layout %s', array('%s' => check_plain($panels->layout)))); - return drupal_not_found(); + else { + $output = var_export($object, TRUE); } - $content_types = panels_get_content_types(); - - foreach ($panels->content as $location => $list) { - foreach ($list as $area) { - $function = $content_types[$area->type]['callback']; - if (function_exists($function)) { - $content[$area->area] .= $function($area->configuration); - } - } + if ($prefix) { + $output = str_replace("\n", "\n$prefix", $output); } - $output = panels_get_layout($layout, $content); return $output; } -function panels_get_layout($layout, $content) { - $output = theme($layout['theme'], check_plain($layout['css_id']), $content); - - if ($output) { - if (file_exists(path_to_theme() . '/' . $layout['css'])) { - drupal_add_css(path_to_theme() . '/' . $layout['css']); - } - else { - drupal_add_css(drupal_get_path('module', $layout['module']) . '/' . $layout['css']); - } - } - return $output; +/** + * Render a display by loading the content into an appropriate + * array and then passing through to panels_render_layout. + * + * if $incoming_content is NULL, default content will be applied. Use + * an empty string to indicate no content. + * @render + * @ingroup hook_invocations + */ +function panels_render_display(&$display) { + panels_load_include('display-render'); + panels_load_include('plugins'); + return _panels_render_display($display); } /** * For external use: Given a layout ID and a $content array, return the - * finished layout. + * panel display. The content array is filled in based upon the content + * available in the layout. If it's a two column with a content + * array defined like array('left' => t('Left side'), 'right' => + * t('Right side')), then the $content array should be array('left' => + * $output_left, 'right' => $output_right) + * @render */ function panels_print_layout($id, $content) { - $layouts = panels_get_layouts(); - $layout = $layouts[$id]; - if (!$layout) { - return; - } - - return panels_get_layout($layout, $content); + panels_load_include('plugins'); + return _panels_print_layout($id, $content); } -// --------------------------------------------------------------------------- -// panels data loading - -function panels_load_includes($directory, $callback) { - // Load all our module 'on behalfs'. - $path = drupal_get_path('module', 'panels') . '/' . $directory; - $files = drupal_system_listing('.inc$', $path, 'name', 0); - - foreach($files as $file) { - require_once('./' . $file->filename); - } - $output = module_invoke_all($callback); - foreach ($files as $file) { - $function = 'panels_' . $file->name . '_' . $callback; - if (function_exists($function)) { - $result = $function(); - if (isset($result) && is_array($result)) { - $output = array_merge($output, $result); - } - } - } - return $output; +// @layout +function panels_print_layout_icon($id, $layout, $title = NULL) { + drupal_add_css(panels_get_path('css/panels_admin.css')); + $file = panels_get_path($layout['icon'], false, $layout['module']); + return theme('panels_layout_icon', $id, theme('image', $file), $title); } -function panels_get_layouts() { - static $layout = NULL; - if (!$layout) { - $layouts = panels_load_includes('layouts', 'panels_layouts'); - } - return $layouts; -} -function panels_get_content_types() { - static $layout = NULL; - if (!$layout) { - $layouts = panels_load_includes('content_types', 'panels_content_types'); +/** + * Theme the layout icon image + * @layout + * @todo move to theme.inc + */ +function theme_panels_layout_icon($id, $image, $title = NULL) { + $output .= '
'; + $output .= $image; + if ($title) { + $output .= '
' . $title . '
'; } - return $layouts; + $output .= '
'; + return $output; } /** - * Helper function for autocompletion of node titles. - * This is mostly stolen from clipper. + * Theme the layout link image + * @layout */ -function panels_node_autocomplete($string) { - if ($string != '') { // if there are node_types passed, we'll use those in a MySQL IN query. - $result = db_query_range(db_rewrite_sql('SELECT n.title, u.name FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE LOWER(title) LIKE LOWER("%%%s%%")'), $string, 0, 10); - $prefix = count($array) ? implode(', ', $array) .', ' : ''; - - $matches = array(); - while ($node = db_fetch_object($result)) { - $n = $node->title; - // Commas and quotes in terms are special cases, so encode 'em. - if (preg_match('/,/', $node->title) || preg_match('/"/', $node->title)) { - $n = '"'. preg_replace('/"/', '""', $node->title) .'"'; - } - $matches[$prefix . $n] = ''. check_plain($node->title) .' ('. t('by %user', array('%user' => check_plain($node->name))) .')'; - } - print drupal_to_js($matches); - exit(); - } +function theme_panels_layout_link($title, $id, $image, $link) { + $output .= ''; + return $output; } diff --git a/panels2.doxy b/panels2.doxy new file mode 100644 index 0000000000000000000000000000000000000000..c8a6bbd205b7e4943a4581762a1e40d9a4cf82f1 --- /dev/null +++ b/panels2.doxy @@ -0,0 +1,292 @@ +# $Id$ +# Doxyfile 1.5.6 + +# MAKE SURE to replace the ${variable} values with settings appropriate +# to your system! + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "Panels 2" +PROJECT_NUMBER = +OUTPUT_DIRECTORY = ${OUTPUT}/panels2 +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = NO +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = YES +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = ${MODULE_PATH} +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = YES +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +ALIASES = "L{1}=\ref @w{\m}\1" \ + "m=panels_" \ + "mA=@w{@m}api_" \ + "mG=@w{@m}glossary_" \ + "mAP=@w{@mA}plugins_" \ + "mGs=@w{@mG}sec_" \ + "mGt=@w{@mG}term_" \ + "LA{1}=\ref @w{@mA}\1" \ + "LAP{1}=\ref @w{@mAP}\1" \ + "LGt{1}=\ref @w{@mGt}\1" \ + "L{2}=\ref @w{@m}\1 \"\2\"" \ + "LA{2}=\ref @w{@mA}\1 \"\2\"" \ + "LAP{2}=\ref @w{@mAP}\1 \"\2\"" \ + "LAP{3}=\ref @w{@mAP}\1_\2 \"\3\"" \ + "LGt{2}=\ref @w{@mGt}\1 \"\2\"" \ + "AG{2}=\par \anchor @w{\mGt}\1 \LITI{\2} \n" \ + "AAP{3}=\anchor @w{\mAP}\1_\2 \3" \ + "SECglossary{2}=\section @w{@mGs}\1 \2" \ + "SSECglossary{3}=\subsection @w{@mGs}\1_\2 \3" \ + "Psubplug{2}=\subpage @w{@mAP}\1 \"\2\"" \ + "Pplug{2}=\page @w{@mAP}\1 \2" \ + "SECplug{3}=\section @w{@mAP}\1_\2 \3" \ + "SSECplug{4}=\subsection @w{@mAP}\1_\2_\3 \4" \ + "S{1}=@w{\1}" \ + "E{1}=@w{\1}" \ + "LITI{1}=@w{\1}:" \ + "IMG{2}=\image html \1 \"\2\"" \ + "TR{1}=@w{\1}" \ + "TD{1}=@w{\1}" \ + "pdarray=\LGt{p-d-array,plugin definition array}" \ + "pdfunc=\LGt{p-d-func,plugin declaration function}" \ + "pdarrays=\LGt{p-d-array,plugin definition arrays}" \ + "pdfuncs=\LGt{p-d-func,plugin declaration functions}" \ + "plugprops=\LGt{plugprop,properties}" \ + "plugprop=\LGt{plugprop,property}" \ + "w{1}=\1" # uber wrapper of godly doxy substitution! +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = YES +SORT_GROUP_NAMES = YES +SORT_BY_SCOPE_NAME = YES +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = YES +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = "sed -n 's/^.*\$Id: [^ \]* \(.*\) Exp.*/\1/p'" +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$line: $text" +WARN_LOGFILE = /var/log/doxy/drupal-doxy.log +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ${MODULE_PATH} +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.module \ + *.theme \ + *.engine \ + *.install \ + *.inc +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = YES +EXCLUDE_PATTERNS = */.svn/* \ + */CVS/* \ + */cvs-release-notes.php \ + */patches/* +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = ${MODULE_PATH}/docs +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = html/images +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = YES +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = ${MODULE_PATH}/docs/doxygen.css +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = +DOCSET_BUNDLE_ID = +HTML_DYNAMIC_SECTIONS = YES +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = YES +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = YES +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = +COMPACT_LATEX = NO +PAPER_TYPE = letter +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = YES +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = YES +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = /usr/local/bin +HIDE_UNDOC_RELATIONS = NO +HAVE_DOT = YES +DOT_FONTNAME = FreeSans +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +CALLER_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = YES +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = YES