summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEarl Miles2006-07-22 04:36:10 (GMT)
committer Earl Miles2006-07-22 04:36:10 (GMT)
commit9f3609c9b0ff370433460f1c621a2fc4d42c4988 (patch)
treebd71a85a07ef375a28c20947b840edc2e8a947e3
Panels module, son of dashboard. Initial commit. Thanks to sepeck and eaton for layout icons!
-rw-r--r--API.txt0
-rw-r--r--INSTALL.txt0
-rw-r--r--README.txt0
-rw-r--r--TODO.txt14
-rw-r--r--content_types/block.inc111
-rw-r--r--content_types/custom.inc90
-rw-r--r--content_types/node.inc97
-rw-r--r--images/blank.gifbin0 -> 62 bytes
-rw-r--r--images/go-bottom.pngbin0 -> 687 bytes
-rw-r--r--images/go-down.pngbin0 -> 680 bytes
-rw-r--r--images/go-top.pngbin0 -> 653 bytes
-rw-r--r--images/go-up.pngbin0 -> 644 bytes
-rw-r--r--images/user-trash.pngbin0 -> 655 bytes
-rw-r--r--layouts/threecol_25_50_25.css16
-rw-r--r--layouts/threecol_25_50_25.inc44
-rw-r--r--layouts/threecol_25_50_25.pngbin0 -> 200 bytes
-rw-r--r--layouts/threecol_25_50_25_stacked.css21
-rw-r--r--layouts/threecol_25_50_25_stacked.inc50
-rw-r--r--layouts/threecol_25_50_25_stacked.pngbin0 -> 208 bytes
-rw-r--r--layouts/threecol_33_34_33.css16
-rw-r--r--layouts/threecol_33_34_33.inc44
-rw-r--r--layouts/threecol_33_34_33.pngbin0 -> 196 bytes
-rw-r--r--layouts/threecol_33_34_33_stacked.css21
-rw-r--r--layouts/threecol_33_34_33_stacked.inc50
-rw-r--r--layouts/threecol_33_34_33_stacked.pngbin0 -> 208 bytes
-rw-r--r--layouts/twocol.css12
-rw-r--r--layouts/twocol.inc41
-rw-r--r--layouts/twocol.pngbin0 -> 193 bytes
-rw-r--r--layouts/twocol_stacked.css17
-rw-r--r--layouts/twocol_stacked.inc47
-rw-r--r--layouts/twocol_stacked.pngbin0 -> 202 bytes
-rw-r--r--panels.install38
-rw-r--r--panels.module799
-rw-r--r--panels_admin.css14
34 files changed, 1542 insertions, 0 deletions
diff --git a/API.txt b/API.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/API.txt
diff --git a/INSTALL.txt b/INSTALL.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/INSTALL.txt
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README.txt
diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 0000000..16a7341
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,14 @@
+TODO:
+
+? content type: phptemplate
+
+content templates:
+ 1 x 2 x 1
+ 3 col -- even
+access control to pages
+access control to content types
+
+dashboard node extension
+
+write help
+document plugin API
diff --git a/content_types/block.inc b/content_types/block.inc
new file mode 100644
index 0000000..0d6e7f5
--- /dev/null
+++ b/content_types/block.inc
@@ -0,0 +1,111 @@
+<?php
+/**
+ * Callback function to supply a list of content types.
+ */
+function panels_block_panels_content_types() {
+ $items['block'] = array(
+ 'callback' => '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 '<strong>Block</strong>: ' . $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'] = '<div class="container-inline">';
+ $form['#suffix'] = '</div>';
+ 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;
+ }
+}
diff --git a/content_types/custom.inc b/content_types/custom.inc
new file mode 100644
index 0000000..2ffafad
--- /dev/null
+++ b/content_types/custom.inc
@@ -0,0 +1,90 @@
+<?php
+/**
+ * Callback function to supply a list of content types.
+ */
+function panels_custom_panels_content_types() {
+ $items['custom'] = array(
+ 'callback' => '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'], FALSE, $conf['format'], false);
+ return theme('panels_content_custom', $title, $body);
+}
+
+function theme_panels_content_custom($title, $body) {
+ $output = '<div class="panel-custom">';
+ if ($title) {
+ $output .= '<h2 class="title">' . $title . '</h2>';
+ }
+ $output .= $body;
+ $output .= '</div>';
+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 '<strong>Custom</strong>: ' . 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'] = '<div class="container-inline">';
+ $form['#suffix'] = '</div>';
+ 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;
+ }
+}
diff --git a/content_types/node.inc b/content_types/node.inc
new file mode 100644
index 0000000..6dc5902
--- /dev/null
+++ b/content_types/node.inc
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Callback function to supply a list of content types.
+ */
+function panels_node_panels_content_types() {
+ $items['node'] = array(
+ 'callback' => '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 '<strong>Node</strong>: ' . 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'] = '<div class="container-inline">';
+ $form['#suffix'] = '</div>';
+ 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;
+ }
+}
diff --git a/images/blank.gif b/images/blank.gif
new file mode 100644
index 0000000..c31552c
--- /dev/null
+++ b/images/blank.gif
Binary files differ
diff --git a/images/go-bottom.png b/images/go-bottom.png
new file mode 100644
index 0000000..4651e18
--- /dev/null
+++ b/images/go-bottom.png
Binary files differ
diff --git a/images/go-down.png b/images/go-down.png
new file mode 100644
index 0000000..678da59
--- /dev/null
+++ b/images/go-down.png
Binary files differ
diff --git a/images/go-top.png b/images/go-top.png
new file mode 100644
index 0000000..eb64c96
--- /dev/null
+++ b/images/go-top.png
Binary files differ
diff --git a/images/go-up.png b/images/go-up.png
new file mode 100644
index 0000000..6d6b619
--- /dev/null
+++ b/images/go-up.png
Binary files differ
diff --git a/images/user-trash.png b/images/user-trash.png
new file mode 100644
index 0000000..0e0953c
--- /dev/null
+++ b/images/user-trash.png
Binary files differ
diff --git a/layouts/threecol_25_50_25.css b/layouts/threecol_25_50_25.css
new file mode 100644
index 0000000..e87c568
--- /dev/null
+++ b/layouts/threecol_25_50_25.css
@@ -0,0 +1,16 @@
+.panel-3col {
+ /* border: 1px solid red; */
+ overflow: hidden;
+}
+.panel-3col .panel-col-first {
+ float: left;
+ width: 25%;
+}
+.panel-3col .panel-col {
+ float: left;
+ width: 50%;
+}
+.panel-3col .panel-col-last {
+ float: left;
+ width: 25%;
+}
diff --git a/layouts/threecol_25_50_25.inc b/layouts/threecol_25_50_25.inc
new file mode 100644
index 0000000..cea508d
--- /dev/null
+++ b/layouts/threecol_25_50_25.inc
@@ -0,0 +1,44 @@
+<?php
+/**
+ * implementation of hook_panels_layouts
+ */
+function panels_threecol_25_50_25_panels_layouts() {
+ $items['threecol_25_50_25'] = array(
+ 'module' => 'panels',
+ 'path' => 'layouts',
+ 'title' => t('Three column 25/50/25'),
+ 'icon' => 'layouts/threecol_25_50_25.png',
+ 'theme' => 'panels_threecol_25_50_25',
+ 'css' => 'layouts/threecol_25_50_25.css',
+ 'content areas' => array('left' => t('Left side'), 'middle' => t('Middle column'), 'right' => t('Right side')),
+ );
+
+ return $items;
+}
+
+/**
+ * This function uses heredoc notation to make it easier to convert
+ * to a template.
+ */
+function theme_panels_threecol_25_50_25($id, $content) {
+ if ($id) {
+ $idstr = " id='$id'";
+ }
+
+ $output = <<<EOT
+<div class="panel-3col" $idstr>
+ <div class="panel-col-first">
+ $content[left]
+ </div>
+
+ <div class="panel-col">
+ $content[middle]
+ </div>
+
+ <div class="panel-col-last">
+ $content[right]
+ </div>
+</div>
+EOT;
+ return $output;
+}
diff --git a/layouts/threecol_25_50_25.png b/layouts/threecol_25_50_25.png
new file mode 100644
index 0000000..ad6832a
--- /dev/null
+++ b/layouts/threecol_25_50_25.png
Binary files differ
diff --git a/layouts/threecol_25_50_25_stacked.css b/layouts/threecol_25_50_25_stacked.css
new file mode 100644
index 0000000..04b7731
--- /dev/null
+++ b/layouts/threecol_25_50_25_stacked.css
@@ -0,0 +1,21 @@
+.panel-3col-stacked {
+ /* border: 1px solid red; */
+ overflow: hidden;
+}
+.panel-3col-stacked .panel-col-top,
+.panel-3col-stacked .panel-col-bottom {
+ width: 100%;
+ clear: both;
+}
+.panel-3col-stacked .panel-col-first {
+ float: left;
+ width: 25%;
+}
+.panel-3col-stacked .panel-col {
+ float: left;
+ width: 50%;
+}
+.panel-3col-stacked .panel-col-last {
+ float: left;
+ width: 25%;
+}
diff --git a/layouts/threecol_25_50_25_stacked.inc b/layouts/threecol_25_50_25_stacked.inc
new file mode 100644
index 0000000..632f07d
--- /dev/null
+++ b/layouts/threecol_25_50_25_stacked.inc
@@ -0,0 +1,50 @@
+<?php
+/**
+ * implementation of hook_panels_layouts
+ */
+function panels_threecol_25_50_25_stacked_panels_layouts() {
+ $items['threecol_25_50_25_stacked'] = array(
+ 'module' => 'panels',
+ 'path' => 'layouts',
+ 'title' => t('Three column 25/50/25 stacked'),
+ 'icon' => 'layouts/threecol_25_50_25_stacked.png',
+ 'theme' => 'panels_threecol_25_50_25_stacked',
+ 'css' => 'layouts/threecol_25_50_25_stacked.css',
+ 'content areas' => array('top' => t('Top'), 'left' => t('Left side'), 'middle' => t('Middle column'), 'right' => t('Right side'), 'bottom' => t('Bottom')),
+ );
+
+ return $items;
+}
+
+/**
+ * This function uses heredoc notation to make it easier to convert
+ * to a template.
+ */
+function theme_panels_threecol_25_50_25_stacked($id, $content) {
+ if ($id) {
+ $idstr = " id='$id'";
+ }
+
+ $output = <<<EOT
+<div class="panel-3col-stacked" $idstr>
+ <div class="panel-col-top">
+ $content[top]
+ </div>
+ <div class="panel-col-first">
+ $content[left]
+ </div>
+
+ <div class="panel-col">
+ $content[middle]
+ </div>
+
+ <div class="panel-col-last">
+ $content[right]
+ </div>
+ <div class="panel-col-bottom">
+ $content[bottom]
+ </div>
+</div>
+EOT;
+ return $output;
+}
diff --git a/layouts/threecol_25_50_25_stacked.png b/layouts/threecol_25_50_25_stacked.png
new file mode 100644
index 0000000..14b4779
--- /dev/null
+++ b/layouts/threecol_25_50_25_stacked.png
Binary files differ
diff --git a/layouts/threecol_33_34_33.css b/layouts/threecol_33_34_33.css
new file mode 100644
index 0000000..c946dd9
--- /dev/null
+++ b/layouts/threecol_33_34_33.css
@@ -0,0 +1,16 @@
+.panel-3col-33 {
+ /* border: 1px solid red; */
+ overflow: hidden;
+}
+.panel-3col-33 .panel-col-first {
+ float: left;
+ width: 33%;
+}
+.panel-3col-33 .panel-col {
+ float: left;
+ width: 34%;
+}
+.panel-3col-33 .panel-col-last {
+ float: left;
+ width: 33%;
+}
diff --git a/layouts/threecol_33_34_33.inc b/layouts/threecol_33_34_33.inc
new file mode 100644
index 0000000..6e274d4
--- /dev/null
+++ b/layouts/threecol_33_34_33.inc
@@ -0,0 +1,44 @@
+<?php
+/**
+ * implementation of hook_panels_layouts
+ */
+function panels_threecol_33_34_33_panels_layouts() {
+ $items['threecol_33_34_33'] = array(
+ 'module' => 'panels',
+ 'path' => 'layouts',
+ 'title' => t('Three column 33/34/33'),
+ 'icon' => 'layouts/threecol_33_34_33.png',
+ 'theme' => 'panels_threecol_33_34_33',
+ 'css' => 'layouts/threecol_33_34_33.css',
+ 'content areas' => array('left' => t('Left side'), 'middle' => t('Middle column'), 'right' => t('Right side')),
+ );
+
+ return $items;
+}
+
+/**
+ * This function uses heredoc notation to make it easier to convert
+ * to a template.
+ */
+function theme_panels_threecol_33_34_33($id, $content) {
+ if ($id) {
+ $idstr = " id='$id'";
+ }
+
+ $output = <<<EOT
+<div class="panel-3col-33" $idstr>
+ <div class="panel-col-first">
+ $content[left]
+ </div>
+
+ <div class="panel-col">
+ $content[middle]
+ </div>
+
+ <div class="panel-col-last">
+ $content[right]
+ </div>
+</div>
+EOT;
+ return $output;
+}
diff --git a/layouts/threecol_33_34_33.png b/layouts/threecol_33_34_33.png
new file mode 100644
index 0000000..468f8bb
--- /dev/null
+++ b/layouts/threecol_33_34_33.png
Binary files differ
diff --git a/layouts/threecol_33_34_33_stacked.css b/layouts/threecol_33_34_33_stacked.css
new file mode 100644
index 0000000..5166624
--- /dev/null
+++ b/layouts/threecol_33_34_33_stacked.css
@@ -0,0 +1,21 @@
+.panel-3col-33-stacked {
+ /* border: 1px solid red; */
+ overflow: hidden;
+}
+.panel-3col-33-stacked .panel-col-top,
+.panel-3col-33-stacked .panel-col-bottom {
+ width: 100%;
+ clear: both;
+}
+.panel-3col-33-stacked .panel-col-first {
+ float: left;
+ width: 33%;
+}
+.panel-3col-33-stacked .panel-col {
+ float: left;
+ width: 34%;
+}
+.panel-3col-33-stacked .panel-col-last {
+ float: left;
+ width: 33%;
+}
diff --git a/layouts/threecol_33_34_33_stacked.inc b/layouts/threecol_33_34_33_stacked.inc
new file mode 100644
index 0000000..38bbcad
--- /dev/null
+++ b/layouts/threecol_33_34_33_stacked.inc
@@ -0,0 +1,50 @@
+<?php
+/**
+ * implementation of hook_panels_layouts
+ */
+function panels_threecol_33_34_33_stacked_panels_layouts() {
+ $items['threecol_33_34_33_stacked'] = array(
+ 'module' => 'panels',
+ 'path' => 'layouts',
+ 'title' => t('Three column 33/34/33 stacked'),
+ 'icon' => 'layouts/threecol_33_34_33_stacked.png',
+ 'theme' => 'panels_threecol_33_34_33_stacked',
+ 'css' => 'layouts/threecol_33_34_33_stacked.css',
+ 'content areas' => array('top' => t('Top'), 'left' => t('Left side'), 'middle' => t('Middle column'), 'right' => t('Right side'), 'bottom' => t('Bottom')),
+ );
+
+ return $items;
+}
+
+/**
+ * This function uses heredoc notation to make it easier to convert
+ * to a template.
+ */
+function theme_panels_threecol_33_34_33_stacked($id, $content) {
+ if ($id) {
+ $idstr = " id='$id'";
+ }
+
+ $output = <<<EOT
+<div class="panel-3col-33-stacked" $idstr>
+ <div class="panel-col-top">
+ $content[top]
+ </div>
+ <div class="panel-col-first">
+ $content[left]
+ </div>
+
+ <div class="panel-col">
+ $content[middle]
+ </div>
+
+ <div class="panel-col-last">
+ $content[right]
+ </div>
+ <div class="panel-col-bottom">
+ $content[bottom]
+ </div>
+</div>
+EOT;
+ return $output;
+}
diff --git a/layouts/threecol_33_34_33_stacked.png b/layouts/threecol_33_34_33_stacked.png
new file mode 100644
index 0000000..ffd1351
--- /dev/null
+++ b/layouts/threecol_33_34_33_stacked.png
Binary files differ
diff --git a/layouts/twocol.css b/layouts/twocol.css
new file mode 100644
index 0000000..94fed95
--- /dev/null
+++ b/layouts/twocol.css
@@ -0,0 +1,12 @@
+.panel-2col {
+ /* border: 1px solid red; */
+ overflow: hidden;
+}
+.panel-2col .panel-col-first {
+ float: left;
+ width: 50%;
+}
+.panel-2col .panel-col-last {
+ float: left;
+ width: 50%;
+}
diff --git a/layouts/twocol.inc b/layouts/twocol.inc
new file mode 100644
index 0000000..0969611
--- /dev/null
+++ b/layouts/twocol.inc
@@ -0,0 +1,41 @@
+<?php
+/**
+ * implementation of hook_panels_layouts
+ */
+function panels_twocol_panels_layouts() {
+ $items['twocol'] = array(
+ 'module' => 'panels',
+ 'path' => 'layouts',
+ 'title' => t('Two column alt'),
+ 'icon' => 'layouts/twocol.png',
+ 'theme' => 'panels_twocol',
+ 'css' => 'layouts/twocol.css',
+ 'content areas' => array('left' => t('Left side'), 'right' => t('Right side')),
+ );
+
+ return $items;
+}
+
+/**
+ * This function uses heredoc notation to make it easier to convert
+ * to a template.
+ */
+function theme_panels_twocol($id, $content) {
+ if ($id) {
+ $idstr = " id='$id'";
+ }
+
+ $output = <<<EOT
+<div class="panel-2col" $idstr>
+ <div class="panel-col-first">
+ $content[left]
+ </div>
+
+ <div class="panel-col-last">
+ $content[right]
+ </div>
+</div>
+<br clear="both" />
+EOT;
+ return $output;
+}
diff --git a/layouts/twocol.png b/layouts/twocol.png
new file mode 100644
index 0000000..9d2965e
--- /dev/null
+++ b/layouts/twocol.png
Binary files differ
diff --git a/layouts/twocol_stacked.css b/layouts/twocol_stacked.css
new file mode 100644
index 0000000..2d708b6
--- /dev/null
+++ b/layouts/twocol_stacked.css
@@ -0,0 +1,17 @@
+.panel-2col-stacked {
+ /* border: 1px solid red; */
+ overflow: hidden;
+}
+.panel-2col-stacked .panel-col-top,
+.panel-2col-stacked .panel-col-bottom {
+ width: 100%;
+ clear: both;
+}
+.panel-2col-stacked .panel-col-first {
+ float: left;
+ width: 50%;
+}
+.panel-2col-stacked .panel-col-last {
+ float: left;
+ width: 50%;
+}
diff --git a/layouts/twocol_stacked.inc b/layouts/twocol_stacked.inc
new file mode 100644
index 0000000..89ec873
--- /dev/null
+++ b/layouts/twocol_stacked.inc
@@ -0,0 +1,47 @@
+<?php
+/**
+ * implementation of hook_panels_layouts
+ */
+function panels_twocol_stacked_panels_layouts() {
+ $items['twocol_stacked'] = array(
+ 'module' => 'panels',
+ 'path' => 'layouts',
+ 'title' => t('Two column stacked'),
+ 'icon' => 'layouts/twocol_stacked.png',
+ 'theme' => 'panels_twocol_stacked',
+ 'css' => 'layouts/twocol_stacked.css',
+ 'content areas' => array('top' => t('Top'), 'left' => t('Left side'), 'right' => t('Right side'), 'bottom' => t('Bottom')),
+ );
+
+ return $items;
+}
+
+/**
+ * This function uses heredoc notation to make it easier to convert
+ * to a template.
+ */
+function theme_panels_twocol_stacked($id, $content) {
+ if ($id) {
+ $idstr = " id='$id'";
+ }
+
+ $output = <<<EOT
+<div class="panel-2col-stacked" $idstr>
+ <div class="panel-col-top">
+ $content[top]
+ </div>
+ <div class="panel-col-first">
+ $content[left]
+ </div>
+
+ <div class="panel-col-last">
+ $content[right]
+ </div>
+ <div class="panel-col-bottom">
+ $content[bottom]
+ </div>
+</div>
+<br clear="both" />
+EOT;
+ return $output;
+}
diff --git a/layouts/twocol_stacked.png b/layouts/twocol_stacked.png
new file mode 100644
index 0000000..30ab8b6
--- /dev/null
+++ b/layouts/twocol_stacked.png
Binary files differ
diff --git a/panels.install b/panels.install
new file mode 100644
index 0000000..b519dfd
--- /dev/null
+++ b/panels.install
@@ -0,0 +1,38 @@
+<?php
+// $Id$
+
+/**
+ * Install the panels tables
+ */
+function panels_install() {
+ switch ($GLOBALS['dbtype']) {
+ case 'pgsql':
+ case 'mysql':
+ case 'mysqli':
+ default:
+ db_query(<<<EOT
+ create table {panels_info} (
+ did int(10) not null default 0 primary key,
+ title varchar(128),
+ access varchar(128),
+ path varchar(128),
+ css_id varchar(128),
+ layout varchar(32)
+ )
+EOT
+ );
+ db_query(<<<EOT
+ create table {panels_area} (
+ did int(10) not null default 0,
+ area varchar(32),
+ type varchar(32),
+ configuration longtext,
+ position int(5),
+ key (did)
+ );
+EOT
+ );
+
+ }
+}
+
diff --git a/panels.module b/panels.module
new file mode 100644
index 0000000..818ab56
--- /dev/null
+++ b/panels.module
@@ -0,0 +1,799 @@
+<?php
+// $Id$
+
+/**
+ * Implementation of hook_help()
+ */
+function panels_help($section = '') {
+ switch ($section) {
+ case 'admin/modules#description':
+ return t('The panels module allows the creation of pages with flexible layouts.');
+ case 'admin/panels':
+ case 'admin/panels/list':
+ return t('<p>You may peruse a list of your current panels layouts and edit them, or click add to create a new page.</p>');
+ case 'admin/panels/add':
+ return t('<p>Choose a layout for your new page from the list below.</p>');
+ }
+}
+
+/**
+ * Implementation of hook_perm()
+ */
+function panels_perm() {
+ return array('create panelss');
+}
+
+/**
+ * Implementation of hook_menu()
+ */
+function panels_menu($may_cache) {
+ if ($may_cache) {
+ $access = user_access('create panelss');
+ $items[] = array(
+ 'path' => 'admin/panels',
+ 'title' => t('panels'),
+ 'access' => $access,
+ 'callback' => 'panels_list_page',
+ );
+ $items[] = array(
+ 'path' => 'admin/panels/list',
+ 'title' => t('list'),
+ 'access' => $access,
+ 'callback' => 'panels_list_page',
+ 'weight' => -10,
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ );
+ $items[] = array(
+ 'path' => 'admin/panels/add',
+ 'title' => t('add'),
+ 'access' => $access,
+ 'callback' => 'panels_add_page',
+ 'type' => MENU_LOCAL_TASK,
+ );
+ $items[] = array(
+ 'path' => 'admin/panels/add/layout',
+ 'title' => t('add'),
+ 'access' => $access,
+ 'callback' => 'panels_add_layout_page',
+ 'type' => MENU_LOCAL_TASK,
+ );
+ $items[] = array(
+ 'path' => 'admin/panels/edit',
+ 'title' => t('edit panels'),
+ 'access' => $access,
+ 'callback' => 'panels_edit_page',
+ 'type' => MENU_CALLBACK,
+ );
+ $items[] = array(
+ 'path' => 'admin/panels/delete',
+ 'title' => t('delete panels'),
+ 'access' => $access,
+ 'callback' => 'panels_delete_page',
+ '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 panelss 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;
+}
+
+/**
+ * Determine whether or not the current user has access to this
+ * panels.
+ */
+function panels_access($access) {
+ // for now
+ return TRUE;
+}
+
+/**
+ * panels path helper function
+ */
+function panels_get_file_path($module, $file, $base_path = true) {
+ if ($base_path) {
+ $output = base_path();
+ }
+ return $output . drupal_get_path('module', $module) . '/' . $file;
+}
+
+// ---------------------------------------------------------------------------
+// panels custom image button
+
+/**
+ * Custom form element to do our nice images.
+ */
+function panels_elements() {
+ $type['panels_imagebutton'] = array('#input' => TRUE, '#button_type' => 'submit',);
+ return $type;
+}
+
+/**
+ * Theme our image button.
+ */
+function theme_panels_imagebutton($element) {
+ return '<input type="image" class="form-'. $element['#button_type'] .'" name="'. $element['#name'] .'" value="'. check_plain($element['#default_value']) .'" '. drupal_attributes($element['#attributes']) . ' src="' . $element['#image'] . '" alt="' . $element['#title'] . '" title="' . $element['#title'] . "\" />\n";
+}
+
+function panels_imagebutton_value() {
+ // null function guarantees default_value doesn't get moved to #value.
+}
+
+/**
+ * Add a single button to a form.
+ */
+function panels_add_button($image, $name, $text) {
+ $module_path = base_path() . drupal_get_path('module', 'panels');
+
+ return array(
+ '#type' => 'panels_imagebutton',
+ '#image' => $module_path . '/images/' . $image,
+ '#title' => $text,
+ '#default_value' => $name,
+ );
+}
+
+/**
+ * Set a button to a blank image -- used for placeholders when buttons are
+ * not relevant but just removing it would be visually unappealing.
+ */
+function panels_set_blank(&$form) {
+ $form['#type'] = 'markup';
+ $form['#value'] = theme('image', drupal_get_path('module', 'panels') . '/images/blank.gif');
+}
+
+// ---------------------------------------------------------------------------
+// panels administrative pages
+
+/**
+ * Provide a list of panelss, with links to edit or delete them.
+ */
+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('edit', "admin/panels/edit/$panels->did"),
+ l('delete', "admin/panels/delete/$panels->did"),
+ ));
+ $items[] = $item;
+ }
+ $header = array(
+ t('panels title'),
+ t('URL'),
+ t('Operations'),
+ );
+ $output = theme('table', $header, $items);
+ return $output;
+}
+
+/*
+ * Provide a form to confirm deletion of a view.
+ */
+function panels_delete_page($did = '') {
+ $panels = panels_load_panels($did);
+
+ if (!$panels) {
+ return 'admin/panels';
+ }
+
+ $form['did'] = array('#type' => 'value', '#value' => $panels->did);
+ return confirm_form('panels_delete_confirm', $form,
+ t('Are you sure you want to delete %title?', array('%title' => $panels->title)),
+ $_GET['destination'] ? $_GET['destination'] : 'admin/panels',
+ t('This action cannot be undone.'),
+ t('Delete'), t('Cancel')
+ );
+}
+
+/*
+ * Handle the submit button to delete a view.
+ */
+function panels_delete_confirm_submit($formid, $form) {
+ if ($form['confirm']) {
+ panels_delete_panels((object) $form);
+ drupal_goto('admin/panels');
+ }
+}
+
+/**
+ * Handle the add panels page
+ */
+function panels_add_page($layout = NULL) {
+ $layouts = panels_get_layouts();
+ theme_add_style(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;
+ }
+ $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;
+ }
+
+ if (!$layouts[$layout]) {
+ return drupal_not_found();
+ }
+
+ $panels->layout = $layout;
+ return panels_edit_form($panels);
+}
+
+function theme_panels_add_image($title, $id, $image) {
+ $output .= '<div class="layout-link">';
+ $output .= $image;
+ $output .= '<div>' . l($title, $_GET['q'] . '/' . $id) . '</div>';
+ $output .= '</div>';
+ return $output;
+}
+// ---------------------------------------------------------------------------
+// panels administrative pages
+
+function panels_edit_page($did = NULL) {
+ if (!$did || !($panels = panels_load_panels($did))) {
+ return drupal_not_found();
+ }
+ return panels_edit_form($panels);
+}
+
+/**
+ * shortcut to ease the syntax of the various form builder tricks we use.
+ */
+function panels_form_builder(&$form, $form_id = 'panels_edit_form') {
+ $form = form_builder($form_id, $form);
+}
+
+/**
+ * Edit an already loaded panels.
+ */
+function panels_edit_form($panels) {
+ theme_add_style(drupal_get_path('module', 'panels') . '/panels_admin.css');
+ $layouts = panels_get_layouts();
+ $layout = $layouts[$panels->layout];
+
+ $content_types = panels_get_content_types();
+
+ // Process all our add button stuff first so we can add stuff to the
+ // form semi dynamically.
+
+ $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.'),
+ );
+
+ $default_radio = array_shift(array_keys($layout['content areas']));
+ $form['add']['area'] = array(
+ '#type' => 'radios',
+ '#title' => t('Area'),
+ '#options' => $layout['content areas'],
+ '#prefix' => '<div class="container-inline">',
+ '#suffix' => '</div>',
+ '#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->conf = $conf;
+ $add->type = $id;
+ $form['add']['area']['#parents'] = array('area');
+ panels_form_builder($form['add']['area']);
+ $add->area = $form_values['area'];
+ }
+ }
+ }
+
+ $form['layout'] = array(
+ '#type' => 'value',
+ '#value' => $panels->layout
+ );
+
+ $form['did'] = array(
+ '#type' => 'value',
+ '#value' => $panels->did,
+ );
+
+ $form['info'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('General information'),
+ '#collapsible' => false,
+ );
+
+ $file = panels_get_file_path($layout['module'], $layout['icon'], false);
+ $icon .= theme('image', $file);
+ $form['info']['layout-icon'] = array(
+ '#value' => '<div class="layout-icon">' . $icon . '</div>',
+ );
+
+ $form['info']['layout-display'] = array(
+ '#value' => '<strong>Layout</strong>: ' . $layout['title'],
+ );
+
+ $form['info']['title'] = array(
+ '#type' => 'textfield',
+ '#default_value' => $panels->title,
+ '#title' => t('Page title'),
+ '#description' => t('The page title for this panels layout'),
+ );
+
+ $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;
+ }
+ $area_record = new stdClass();
+ $area_record->type = $add->type;
+ $area_record->configuration = $add->conf;
+
+ $panels->content[$area][$position] = $area_record;
+ $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']);
+ }
+ }
+ }
+ }
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ );
+
+ return drupal_get_form('panels_edit_form', $form);
+}
+
+/**
+ * Display the form to edit a panels.
+ */
+function theme_panels_edit_form($form) {
+ $layouts = panels_get_layouts();
+ $layout = $layouts[$form['layout']['#value']];
+
+ $content_types = panels_get_content_types();
+
+ $output .= form_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' => form_render($form['content'][$area][$id]['configuration']),
+ '#collapsible' => true,
+ '#collapsed' => true
+ );
+ $buttons = form_render($form['content'][$area][$id]['buttons']);
+ $area_content .= $buttons . '&nbsp;' . $function('list', $conf) .
+ theme('fieldset', $fieldset) /* . '<br />' */;
+ }
+ }
+ }
+ $content[$area] = theme('fieldset', array('#title' => check_plain($title), '#value' => $area_content));
+ }
+
+ $output .= panels_get_layout($layout, $content);
+
+ $output .= form_render($form);
+ return $output;
+}
+
+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']);
+ }
+ }
+ }
+ }
+}
+
+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];
+ }
+ }
+ $panels->content[$area] = $array;
+ }
+ panels_save_panels($panels);
+ drupal_set_message(t('The panels has been saved.'));
+ return 'admin/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'));
+ }
+// 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);
+}
+
+/**
+ * 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;
+}
+
+/**
+ * move an item in an array up one position
+ */
+function panels_move_down(&$array, &$position) {
+ $value = $array[$position];
+ $array[$position] = $array[$position + 1];
+ $array[$position + 1] = $value;
+}
+
+/**
+ * Remove an item from an array
+ */
+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;
+ }
+ 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);
+ }
+ }
+ }
+ cache_clear_all('menu:', TRUE);
+}
+
+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);
+ cache_clear_all('menu:', TRUE);
+}
+// ---------------------------------------------------------------------------
+// panels page
+
+function panels_panels_page($did) {
+ $panels = panels_load_panels($did);
+ if (!$panels) {
+ return drupal_not_found();
+ }
+
+ $layouts = panels_get_layouts();
+ $layout = $layouts[$panels->layout];
+
+ if (!$layout) {
+ watchdog('panels', t('Unable to find requested layout %s', array('%s' => check_plain($panels->layout))));
+ return drupal_not_found();
+ }
+
+ $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);
+ }
+ }
+ }
+ $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'])) {
+ theme_add_style(path_to_theme() . '/' . $layout['css']);
+ }
+ else {
+ theme_add_style(drupal_get_path('module', $layout['module']) . '/' . $layout['css']);
+ }
+ }
+ return $output;
+}
+
+/**
+ * For external use: Given a layout ID and a $content array, return the
+ * finished layout.
+ */
+function panels_print_layout($id, $content) {
+ $layouts = panels_get_layouts();
+ $layout = $layouts[$id];
+ if (!$layout) {
+ return;
+ }
+
+ return panels_get_layout($layout, $content);
+}
+
+// ---------------------------------------------------------------------------
+// panels data loading
+
+function panels_load_includes($directory, $callback) {
+ // Load all our module 'on behalfs'.
+ $path = drupal_get_path('module', 'panels') . '/' . $directory;
+ $files = 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;
+}
+
+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');
+ }
+ return $layouts;
+}
+
+/**
+ * Helper function for autocompletion of node titles.
+ * This is mostly stolen from clipper.
+ */
+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] = '<span class="autocomplete_title">'. check_plain($node->title) .'</span> <span class="autocomplete_user">('. t('by %user', array('%user' => check_plain($node->name))) .')</span>';
+ }
+ print drupal_to_js($matches);
+ exit();
+ }
+}
diff --git a/panels_admin.css b/panels_admin.css
new file mode 100644
index 0000000..62b981c
--- /dev/null
+++ b/panels_admin.css
@@ -0,0 +1,14 @@
+.layout-link {
+ float: left;
+ padding: 1em;
+ width: 125px;
+}
+
+.layout-link img {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.layout-icon {
+ float: right;
+} \ No newline at end of file