summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kremmel <xing@synapse.plus.com>2005-08-21 16:22:48 +0000
committerMax Kremmel <xing@synapse.plus.com>2005-08-21 16:22:48 +0000
commit7d183a779b42072de4a913029a7daad9f5b30034 (patch)
tree2a21a8105228e1268ab33b0abe061b52b9475f7a
downloadpigeonholes-7d183a779b42072de4a913029a7daad9f5b30034.tar.gz
pigeonholes-7d183a779b42072de4a913029a7daad9f5b30034.tar.bz2
pigeonholes-7d183a779b42072de4a913029a7daad9f5b30034.zip
import pigeonholes - take 2
-rw-r--r--.htaccess9
-rw-r--r--Pigeonholes.php747
-rw-r--r--admin/admin_pigeonholes_inc.php41
-rw-r--r--admin/schema_inc.php54
-rw-r--r--assign_non_members.php84
-rw-r--r--bit_setup_inc.php31
-rw-r--r--edit_pigeonholes.php146
-rw-r--r--edit_structure.php31
-rw-r--r--icons/organise.pngbin0 -> 545 bytes
-rw-r--r--icons/pkg_pigeonholes.pngbin0 -> 3825 bytes
-rw-r--r--index.php25
-rw-r--r--list.php49
-rw-r--r--lookup_pigeonholes_inc.php21
-rw-r--r--servicefunctions_inc.php138
-rw-r--r--templates/admin_pigeonholes.tpl29
-rw-r--r--templates/assign_non_members.tpl112
-rw-r--r--templates/display_members.tpl54
-rw-r--r--templates/display_paths.tpl9
-rw-r--r--templates/edit_pigeonholes.tpl19
-rw-r--r--templates/edit_pigeonholes_inc.tpl55
-rw-r--r--templates/edit_structure.tpl11
-rw-r--r--templates/list.tpl55
-rw-r--r--templates/menu_pigeonholes.tpl26
-rw-r--r--templates/menu_pigeonholes_admin.tpl1
-rw-r--r--templates/pigeonholes_input_inc.tpl18
-rw-r--r--templates/structure_section_inc.tpl97
-rw-r--r--templates/view_structure.tpl15
-rw-r--r--templates/view_structure_inc.tpl26
-rw-r--r--view.php46
29 files changed, 1949 insertions, 0 deletions
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..3bd1bde
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,9 @@
+<IfModule mod_rewrite.c>
+ RewriteEngine on
+ RewriteBase /pigeonholes/
+ RewriteCond %{SCRIPT_FILENAME} -f [OR]
+ RewriteCond %{SCRIPT_FILENAME}/index.php -f
+ RewriteRule ^(.*)$ - [L]
+
+ RewriteRule ^(.*)$ index.php?content_id=$1 [L]
+</IfModule>
diff --git a/Pigeonholes.php b/Pigeonholes.php
new file mode 100644
index 0000000..3d8febf
--- /dev/null
+++ b/Pigeonholes.php
@@ -0,0 +1,747 @@
+<?php
+/**
+ * @version $Header: /cvsroot/bitweaver/_bit_pigeonholes/Pigeonholes.php,v 1.1 2005/08/21 16:22:44 squareing Exp $
+ *
+ * +----------------------------------------------------------------------+
+ * | Copyright ( c ) 2004, bitweaver.org
+ * +----------------------------------------------------------------------+
+ * | All Rights Reserved. See copyright.txt for details and a complete list of authors.
+ * | Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details
+ * |
+ * | For comments, please use phpdocu.sourceforge.net documentation standards!!!
+ * | -> see http://phpdocu.sourceforge.net/
+ * +----------------------------------------------------------------------+
+ * | Authors: xing <xing@synapse.plus.com>
+ * +----------------------------------------------------------------------+
+ *
+ * Pigeonholes class
+ *
+ * @author xing <xing@synapse.plus.com>
+ * @version $Revision: 1.1 $
+ * @package pigeonholes
+ */
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_PATH.'LibertyAttachable.php' );
+require_once( LIBERTY_PKG_PATH.'LibertyStructure.php' );
+
+/**
+ * Pigeonholes
+ *
+ * @package pigeonholes
+ */
+class Pigeonholes extends LibertyAttachable {
+ /**
+ * initiate class
+ * @param $pContentId content id of the pigeonhole - use either one of the ids.
+ * @param $pStructureId structure id of the pigeonhole - use either one of the ids.
+ * @param $pAutoLoad boolean - if set to FALSE, no pigeonhole data is loaded
+ * @param $pExtras boolean - if set to TRUE, pigeonhole content is added as well - 1 additional db access
+ * @return none
+ * @access public
+ **/
+ function Pigeonholes( $pStructureId=NULL, $pContentId=NULL, $pAutoLoad=TRUE, $pExtras=FALSE ) {
+ LibertyAttachable::LibertyAttachable();
+ $this->registerContentType( PIGEONHOLES_CONTENT_TYPE_GUID, array(
+ 'content_type_guid' => PIGEONHOLES_CONTENT_TYPE_GUID,
+ 'content_description' => 'Pigeonhole',
+ 'handler_class' => 'Pigeonholes',
+ 'handler_package' => 'pigeonholes',
+ 'handler_file' => 'Pigeonholes.php',
+ 'maintainer_url' => 'http://www.bitweaver.org'
+ ) );
+ $this->mContentId = $pContentId;
+ $this->mStructureId = $pStructureId;
+ $this->mContentTypeGuid = PIGEONHOLES_CONTENT_TYPE_GUID;
+ if( $pAutoLoad ) {
+ $this->load( $pExtras );
+ }
+ }
+
+ /**
+ * load the pigeonhole
+ * @param $pExtras boolean - if set to true, pigeonhole content is added as well
+ * @return bool TRUE on success, FALSE if it's not valid
+ * @access public
+ **/
+ function load( $pExtras=FALSE ) {
+ if( $this->verifyId( $this->mContentId ) || $this->verifyId( $this->mStructureId ) ) {
+ global $gBitSystem;
+ $lookupColumn = ( !empty( $this->mContentId ) ? 'tc.`content_id`' : 'ts.`structure_id`' );
+ $lookupId = ( !empty( $this->mContentId ) ? $this->mContentId : $this->mStructureId );
+ $query = "SELECT bp.*, ts.`root_structure_id`, ts.`parent_id`, tc.`title`, tc.`data`, tc.`content_type_guid`,
+ uue.`login` AS modifier_user, uue.`real_name` AS modifier_real_name,
+ uuc.`login` AS creator_user, uuc.`real_name` AS creator_real_name
+ FROM `".BIT_DB_PREFIX."bit_pigeonholes` bp
+ INNER JOIN `".BIT_DB_PREFIX."tiki_content` tc ON ( tc.`content_id` = bp.`content_id` )
+ LEFT JOIN `".BIT_DB_PREFIX."tiki_structures` ts ON ( ts.`structure_id` = bp.`structure_id` )
+ LEFT JOIN `".BIT_DB_PREFIX."users_users` uue ON ( uue.`user_id` = tc.`modifier_user_id` )
+ LEFT JOIN `".BIT_DB_PREFIX."users_users` uuc ON ( uuc.`user_id` = tc.`user_id` )
+ WHERE $lookupColumn=?";
+ $result = $this->mDb->query( $query, array( $lookupId ) );
+
+ if ( $result && $result->numRows() ) {
+ $this->mInfo = $result->fields;
+ $this->mContentId = $result->fields['content_id'];
+ $this->mStructureId = $result->fields['structure_id'];
+ $this->mInfo['creator'] = ( isset( $result->fields['creator_real_name'] ) ? $result->fields['creator_real_name'] : $result->fields['creator_user'] );
+ $this->mInfo['editor'] = ( isset( $result->fields['modifier_real_name'] ) ? $result->fields['modifier_real_name'] : $result->fields['modifier_user'] );
+ $this->mInfo['display_link'] = $this->getDisplayLink();
+ }
+
+ // if the content for the pigeonhole is requested, get it
+ if( $pExtras ) {
+ $this->mInfo['path'] = $this->getPigeonholePath();
+ $this->mInfo['display_path'] = $this->getDisplayPath( $this->mInfo['path'] );
+ $this->mInfo['members'] = $this->getPigeonholeMembers();
+ $this->mInfo['members_count'] = count( $this->mInfo['members'] );
+ }
+ }
+ return( count( $this->mInfo ) );
+ }
+
+ /**
+ * get all content inserted in a given pigeonhole. if no id is given, it gets all content for all pigeonholes
+ * @param $pContentId content id of the pigeonhole
+ * @return array of pigeonhole members with according title and content type guid
+ * @access public
+ **/
+ function getPigeonholeMembers( $pContentId=NULL ) {
+ global $gBitUser, $gLibertySystem, $gBitSystem;
+ $ret = FALSE;
+ if( !empty( $this->mContentId ) || !empty( $pContentId ) ) {
+ if( $gBitSystem->isFeatureActive( 'custom_member_sorting' ) ) {
+ $order = "ORDER BY bpm.`pos` ASC";
+ } else {
+ $order = "ORDER BY tc.`content_type_guid`, tc.`title` ASC";
+ }
+
+ $bindVars[] = $this->verifyId( $pContentId ) ? $pContentId : $this->mContentId;
+ $ret = array();
+ $query = "SELECT bpm.*, tc.`content_id`, tc.`user_id`, tc.`title`, tc.`content_type_guid`, uu.`login`, uu.`real_name`
+ FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` bpm
+ RIGHT JOIN `".BIT_DB_PREFIX."bit_pigeonholes` bp ON ( bp.`content_id` = bpm.`parent_id` )
+ INNER JOIN `".BIT_DB_PREFIX."tiki_content` tc ON ( tc.`content_id` = bpm.`content_id` )
+ LEFT JOIN `".BIT_DB_PREFIX."users_users` uu ON ( uu.`user_id` = tc.`user_id` )
+ WHERE bp.`content_id`=?
+ $order";
+ $result = $this->mDb->query( $query, $bindVars );
+ $contentTypes = $gLibertySystem->mContentTypes;
+ while( !$result->EOF ) {
+ $i = $result->fields['content_id'];
+ $ret[$i] = $result->fields;
+ if( !empty( $contentTypes[$ret[$i]['content_type_guid']] ) ) {
+ $type = &$contentTypes[$ret[$i]['content_type_guid']];
+ if( empty( $type['content_object'] ) ) {
+ // create *one* object for each object *type* to call virtual methods.
+ include_once( $gBitSystem->mPackages[$type['handler_package']]['path'].$type['handler_file'] );
+ $type['content_object'] = new $type['handler_class']();
+ }
+ $ret[$i]['display_link'] = $type['content_object']->getDisplayLink( $ret[$i]['title'], $ret[$i] );
+ $ret[$i]['title'] = $type['content_object']->getTitle( $ret[$i] );
+ }
+ $result->MoveNext();
+ }
+ } else {
+ $this->mErrors['get_members'] = tra( 'No valid pigeonhole id given to collect members from.' );
+ }
+ return( !empty( $ret ) ? $ret : NULL );
+ }
+
+ /**
+ * get all items that are not part of a pigeonhole yet
+ * @param $pContentType content type guid of items to be collected. if empty, all content is collected
+ * @param $pIncludeMembers if set to TRUE (boolean), it will return members as well, where the pigeonholes they are assigned to, are in the sub array 'assigned'
+ * @return array of content not in any pigeonhole yet
+ * @access public
+ **/
+ function getNonPigeonholeMembers( $pListHash=NULL, $pContentType=NULL, $pIncludeMembers=FALSE ) {
+ global $gBitUser, $gLibertySystem, $gBitSystem;
+ $where = '';
+ $bindVars = array();
+
+ if( !$pIncludeMembers ) {
+ $where .= "WHERE tc.`content_id` NOT IN ( SELECT DISTINCT `content_id` FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` )";
+ }
+
+ if( !empty( $pListHash['find'] ) && is_string( $pListHash['find'] ) ) {
+ $where .= empty( $where ) ? ' WHERE ' : ' AND ';
+ $where .= " UPPER( tc.`title` ) LIKE ?";
+ $bindVars[] = ( '%'.strtoupper( $pListHash['find'] ).'%');
+ }
+
+ if( $pContentType ) {
+ $where .= empty( $where ) ? ' WHERE ' : ' AND ';
+ $where .= " tc.`content_type_guid`=?";
+ $bindVars[] = $pContentType;
+ }
+
+ if( !empty( $pListHash['sort_mode'] ) ) {
+ $where .= " ORDER BY ".$this->mDb->convert_sortmode( $pListHash['sort_mode'] )." ";
+ } else {
+ $where .= " ORDER BY tc.`content_type_guid`, tc.`title` ASC";
+ }
+
+ $query = "SELECT bpm.`parent_id`, tc.`content_id`, tc.`user_id`, tc.`title`, tc.`content_type_guid`, uu.`login`, uu.`real_name`
+ FROM `".BIT_DB_PREFIX."tiki_content` tc
+ LEFT JOIN `".BIT_DB_PREFIX."bit_pigeonhole_members` bpm ON ( bpm.`content_id` = tc.`content_id` )
+ LEFT JOIN `".BIT_DB_PREFIX."users_users` uu ON ( uu.`user_id` = tc.`user_id` )
+ $where";
+ $result = $this->mDb->query( $query, $bindVars, empty( $pListHash['max_rows'] ) ? NULL : $pListHash['max_rows'] );
+
+ $contentTypes = $gLibertySystem->mContentTypes;
+ while( !$result->EOF ) {
+ $i = $result->fields['content_id'];
+ $ret[$i] = $result->fields;
+ if( !empty( $contentTypes[$ret[$i]['content_type_guid']] ) ) {
+ $type = &$contentTypes[$ret[$i]['content_type_guid']];
+ if( empty( $type['content_object'] ) ) {
+ // create *one* object for each object *type* to call virtual methods.
+ include_once( $gBitSystem->mPackages[$type['handler_package']]['path'].$type['handler_file'] );
+ $type['content_object'] = new $type['handler_class']();
+ }
+ $ret[$i]['display_link'] = $type['content_object']->getDisplayLink( $ret[$i]['title'], $ret[$i] );
+ $ret[$i]['title'] = $type['content_object']->getTitle( $ret[$i] );
+ }
+
+ // generate a map of what items are assigned to what pigeonholes
+ if( $pIncludeMembers && !empty( $result->fields['parent_id'] ) ) {
+ $map[$i][] = $result->fields['parent_id'];
+ }
+
+ $result->MoveNext();
+ }
+
+ // complete the output
+ if( $pIncludeMembers && !empty( $ret ) ) {
+ foreach( $ret as $i => $r ) {
+ $ret[$i]['assigned'] = !empty( $map[$i] ) ? $map[$i] : NULL;
+ }
+ }
+
+ return( !empty( $ret ) ? $ret : NULL );
+ }
+
+ /**
+ * get an array of paths for all pigeonholes. used for pages where data can be inserted into pigeonholes
+ * @param $pContentId content id of pigeonhole.
+ * @return path in form of an array on success, FALSE ( boolean ) if content is in no pigeonhole
+ * @access public
+ * @TODO sort the array somehow to make sure that the path is always incrementing and looks nice...
+ **/
+ function getPigeonholesPathList( $pContentId=NULL ) {
+ $query = "SELECT bp.`content_id`, bp.`structure_id`
+ FROM `".BIT_DB_PREFIX."bit_pigeonholes` bp
+ ORDER BY bp.`content_id` ASC";
+ $result = $this->mDb->query( $query );
+ $pigeonholes = $result->getRows();
+ foreach( $pigeonholes as $pigeonhole ) {
+ $ret[$pigeonhole['content_id']] = $this->getPigeonholePath( $pigeonhole['structure_id'] );
+ }
+
+ if( !empty( $pContentId ) && $assigned = $this->getPigeonholesFromContentId( $pContentId ) ) {
+ foreach( $assigned as $a ) {
+ $ret[$a['content_id']][0]['selected'] = TRUE;
+ }
+ }
+
+ return( !empty( $ret ) ? $ret : FALSE );
+ }
+
+ /**
+ * get all pigeonholes where the contenent has been inserted
+ * @param $pContentId content id of item in question
+ * @return basic information about item requested
+ * @access public
+ **/
+ function getPigeonholesFromContentId( $pContentId ) {
+ if( $this->verifyId( $pContentId ) ) {
+ $query = "SELECT bp.*
+ FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` bpm
+ INNER JOIN `".BIT_DB_PREFIX."bit_pigeonholes` bp ON ( bp.`content_id` = bpm.`parent_id` )
+ WHERE bpm.`content_id`=?";
+ $result = $this->mDb->query( $query, array( $pContentId ) );
+ $ret = $result->getRows();
+ }
+ return( !empty( $ret ) ? $ret : FALSE );
+ }
+
+ /**
+ * get the path of a pigeonhole
+ * @param $pStructureId structure id of pigeonhole, if no id is given, it gets the id from $this->mStructureId
+ * @return path in form of an array
+ * @access public
+ **/
+ function getPigeonholePath( $pStructureId=NULL ) {
+ if( !$this->verifyId( $pStructureId ) ) {
+ $pStructureId = $this->mStructureId;
+ }
+
+ if( $this->verifyId( $pStructureId ) ) {
+ global $gStructure;
+ // create new object if needed
+ if( empty( $gStructure ) ) {
+ $gStructure = new LibertyStructure();
+ }
+ // get the structure path
+ $ret = $gStructure->getPath( $pStructureId );
+ }
+ return( !empty( $ret ) ? $ret : FALSE );
+ }
+
+ /**
+ * Converts a structure path into valid html links
+ * @param $pPath path given by getPigenholePath()
+ * @return the link to display the page.
+ */
+ function getDisplayPath( $pPath ) {
+ $ret = '';
+ if( !empty( $pPath ) && is_array( $pPath ) ) {
+ foreach( $pPath as $node ) {
+ $ret .= ( !empty( $node['parent_id'] ) ? ' &raquo; ' : '' ).'<a href="'.PIGEONHOLES_PKG_URL.'view.php?structure_id='.$node['structure_id'].'">'.$node['title'].'</a>';
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * get list of all pigeonholes
+ * @param $pListHash contains array of items used to limit search results
+ * @param $pListHash[sort_mode] column and orientation by which search results are sorted
+ * @param $pListHash[find] search for a pigeonhole title - case insensitive
+ * @param $pListHash[max_rows] maximum number of rows to return
+ * @param $pListHash[offset] number of results data is offset by
+ * @return array of pigeonholes in 'data' and count of pigeonholes in 'cant'
+ * @access public
+ **/
+ function getList( $pListHash=NULL, $pOnlyGetRoot=FALSE, $pExtras=FALSE ) {
+ $bindVars = array();
+ $where = '';
+ if( !empty( $pListHash['find'] ) ) {
+ $where .= " WHERE UPPER( tc.`title` ) LIKE ? ";
+ $bindVars[] = '%'.strtoupper( $pListHash['find'] ).'%';
+ }
+
+ if( !empty( $pListHash['root_structure_id'] ) && $this->verifyId( $pListHash['root_structure_id'] ) ) {
+ $where .= empty( $where ) ? ' WHERE ' : ' AND ';
+ $where .= " ts.`root_structure_id`=? ";
+ $bindVars[] = $pListHash['root_structure_id'];
+ }
+
+ if( $pOnlyGetRoot ) {
+ $where .= empty( $where ) ? ' WHERE ' : ' AND ';
+ $where .= " ts.`structure_id`=ts.`root_structure_id` ";
+ }
+
+ if( !empty( $pListHash['sort_mode'] ) ) {
+ $where .= " ORDER BY ".$this->mDb->convert_sortmode( $pListHash['sort_mode'] )." ";
+ }
+
+ $query = "SELECT bp.`content_id`,
+ uue.`login` AS modifier_user, uue.`real_name` AS modifier_real_name,
+ uuc.`login` AS creator_user, uuc.`real_name` AS creator_real_name
+ FROM `".BIT_DB_PREFIX."bit_pigeonholes` bp
+ INNER JOIN `".BIT_DB_PREFIX."tiki_content` tc ON ( tc.`content_id` = bp.`content_id` )
+ LEFT JOIN `".BIT_DB_PREFIX."users_users` uue ON ( uue.`user_id` = tc.`modifier_user_id` )
+ LEFT JOIN `".BIT_DB_PREFIX."users_users` uuc ON ( uuc.`user_id` = tc.`user_id` )
+ INNER JOIN `".BIT_DB_PREFIX."tiki_structures` ts ON ( ts.`structure_id` = bp.`structure_id` )
+ $where";
+
+ if( isset( $pListHash['max_rows'] ) && is_numeric( $pListHash['max_rows'] ) && isset( $pListHash['offset'] ) && is_numeric( $pListHash['offset'] ) ) {
+ $result = $this->mDb->query( $query, $bindVars, $pListHash['max_rows'], $pListHash['offset'] );
+ } else {
+ $result = $this->mDb->query( $query, $bindVars );
+ }
+
+ $pigeonIds = $result->getRows();
+ $ret['data'] = array();
+ foreach( $pigeonIds as $id ) {
+ $tmpPigeon = new Pigeonholes( NULL, $id['content_id'], TRUE, $pExtras );
+ $ret['data'][] = $tmpPigeon->mInfo;
+ }
+
+ $query = "SELECT COUNT( bp.`content_id` )
+ FROM `".BIT_DB_PREFIX."bit_pigeonholes` bp
+ INNER JOIN `".BIT_DB_PREFIX."tiki_structures` ts ON ( ts.`structure_id` = bp.`structure_id` )
+ WHERE ts.`structure_id`=ts.`root_structure_id`";
+ $ret['cant'] = $this->mDb->getOne( $query );
+
+ return $ret;
+ }
+
+ /**
+ * Store pigeonhole data
+ * @param $pParamHash contains all data to store the pigeonholes
+ * @param $pParamHash[title] title of the new pigeonhole
+ * @param $pParamHash[edit] description of the pigeonhole
+ * @param $pParamHash[members] array of content_ids that are associated with this pigeonhole
+ * @param $pParamHash[root_structure_id] if this is set, it will add the pigeonhole to this structure. if it's not set, a new structure / top level pigeonhole is created
+ * @param $pParamHash[parent_id] set the structure_id that will server as the parent in the structure
+ * @return bool TRUE on success, FALSE if store could not occur. If FALSE, $this->mErrors will have reason why
+ * @access public
+ **/
+ function store( &$pParamHash ) {
+ if( $this->verify( $pParamHash ) && LibertyAttachable::store( $pParamHash ) ) {
+ $table = BIT_DB_PREFIX."bit_pigeonholes";
+ $this->mDb->StartTrans();
+
+ // this really confusing, strange order way of saving items is due to strange behaviour of GenID
+ // probably has to do with not null default nextval('public.tiki_structures_structure_id_seq'::text)
+ if( $this->mContentId ) {
+ if( !empty( $pParamHash['pigeonhole_store'] ) ) {
+ $locId = array ( "name" => "content_id", "value" => $this->mContentId );
+ $result = $this->mDb->associateUpdate( $table, $pParamHash['pigeonhole_store'], $locId );
+ }
+ $pParamHash['structure_location_id'] = $this->mStructureId;
+ } else {
+ // update the pigeonhole_store and structure_store content_id with the one from LibertyAttachable::store()
+ $pParamHash['structure_store']['content_id'] = $pParamHash['content_id'];
+ $pParamHash['pigeonhole_store']['content_id'] = $pParamHash['content_id'];
+
+ // we need to store the new structure node now
+ global $gStructure;
+ // create new object if needed
+ if( empty( $gStructure ) ) {
+ $gStructure = new LibertyStructure();
+ }
+ $pParamHash['structure_location_id'] = $gStructure->storeNode( $pParamHash['structure_store'] );
+
+ // get the corrent structure_id
+ // structure_id has to be done like this since it's screwed up in the schema
+ $pParamHash['pigeonhole_store']['structure_id'] = $this->mDb->getOne( "SELECT MAX( `structure_id` ) FROM `".BIT_DB_PREFIX."tiki_structures`" );
+ $this->mContentId = $pParamHash['pigeonhole_store']['content_id'];
+ $result = $this->mDb->associateInsert( $table, $pParamHash['pigeonhole_store'] );
+ }
+
+ // store content items
+ if( !empty( $pParamHash['pigeonhole_members_store'] ) ) {
+ // remove items first
+ $this->expungePigeonholeMember( $this->mContentId );
+ if( !$this->insertPigeonholeMember( $pParamHash['pigeonhole_members_store'] ) ) {
+ $this->mErrors['store'] = 'The content could not be inserted into the respective categories.';
+ }
+ }
+
+ $this->mDb->CompleteTrans();
+ $this->load();
+ }
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * verify, clean up and prepare data to be stored
+ * @param $pParamHash all information that is being stored. will update $pParamHash by reference with fixed array of itmes
+ * @return bool TRUE on success, FALSE if store could not occur. If FALSE, $this->mErrors will have reason why
+ * @access private
+ **/
+ function verify( &$pParamHash ) {
+ // make sure we're all loaded up if everything is valid
+ if( $this->isValid() && empty( $this->mInfo ) ) {
+ $this->load( TRUE );
+ }
+
+ // It is possible a derived class set this to something different
+ if( empty( $pParamHash['content_type_guid'] ) ) {
+ $pParamHash['content_type_guid'] = $this->mContentTypeGuid;
+ }
+
+ if( !empty( $this->mContentId ) ) {
+ $pParamHash['content_id'] = $this->mContentId;
+ }
+
+ // content store
+ // check for name issues, first truncate length if too long
+ if( !empty( $pParamHash['title'] ) ) {
+ if( empty( $this->mContentId ) ) {
+ if( empty( $pParamHash['title'] ) ) {
+ $this->mErrors['title'] = 'You must enter a name for this category.';
+ } else {
+ $pParamHash['content_store']['title'] = substr( $pParamHash['title'], 0, 160 );
+ }
+ } else {
+ $pParamHash['content_store']['title'] = ( isset( $pParamHash['title'] ) ) ? substr( $pParamHash['title'], 0, 160 ) : $this->mPigeonholeName;
+ }
+ } elseif( empty( $pParamHash['title'] ) ) {
+ // no name specified
+ $this->mErrors['title'] = 'You must specify a name';
+ }
+
+ // sort out the description
+ if( $this->isValid() && !empty( $this->mInfo['data'] ) && empty( $pParamHash['edit'] ) ) {
+ $pParamHash['edit'] = '';
+ } elseif( empty( $pParamHash['edit'] ) ) {
+ unset( $pParamHash['edit'] );
+ } else {
+ $pParamHash['edit'] = substr( $pParamHash['edit'], 0, 250 );
+ }
+
+ // pigeonhole member store
+ // work out what to do with the content of the pigeonhole
+ if( $this->isValid() && !empty( $this->mInfo['members'] ) && empty( $pParamHash['members'] ) ) {
+ $pParamHash['pigeonhole_members_store']['members'] = '';
+ } elseif( empty( $pParamHash['members'] ) ) {
+ unset( $pParamHash['members'] );
+ } else {
+ $i = 1;
+ $pos = 1;
+
+ // if this is not the first save, we need to get positional data from members and insert them
+ if( !empty( $this->mContentId ) ) {
+ $members = $this->getPigeonholeMembers( $this->mContentId );
+ $pos = count( $members ) + 1;
+ }
+
+ foreach( $pParamHash['members'] as $c_id ) {
+ if( !empty( $members[$c_id]['pos'] ) ) {
+ $pParamHash['pigeonhole_members_store'][$i]['pos'] = $members[$c_id]['pos'];
+ } else {
+ $pParamHash['pigeonhole_members_store'][$i]['pos'] = $pos++;
+ }
+ $pParamHash['pigeonhole_members_store'][$i]['content_id'] = $c_id;
+ $i++;
+ }
+ }
+
+ // structure store
+ if( !empty( $pParamHash['root_structure_id'] ) && $this->verifyId( $pParamHash['root_structure_id'] ) ) {
+ $pParamHash['structure_store']['root_structure_id'] = $pParamHash['root_structure_id'];
+ } else {
+ $pParamHash['structure_store']['root_structure_id'] = NULL;
+ }
+
+ if( !empty( $pParamHash['parent_id'] ) && $this->verifyId( $pParamHash['parent_id'] ) ) {
+ $pParamHash['structure_store']['parent_id'] = $pParamHash['parent_id'];
+ } else {
+ $pParamHash['structure_store']['parent_id'] = NULL;
+ }
+
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * Move content member either up or down when using custom sorting
+ * @param $pParentId pigeonhole id the member belongs to
+ * @param $pMemberId content id of the pigeonhole member
+ * @param $pOrientation requires either north or south as value
+ * @return bool TRUE on success, FALSE if store could not occur. If FALSE, $this->mErrors will have reason why
+ * @access public
+ **/
+ function moveMember( $pParentId, $pMemberId, $pOrientation ) {
+ if( $this->isValid() && !empty( $pParentId ) && is_numeric( $pParentId ) && !empty( $pMemberId ) && is_numeric( $pMemberId ) ) {
+ if( !empty( $pOrientation ) && $pOrientation == 'north' ) {
+ $query = "SELECT `parent_id`, `content_id`, `pos` FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` WHERE `pos`<? AND `parent_id`=? ORDER BY `pos` DESC";
+ } elseif ( !empty( $pOrientation ) && $pOrientation == 'south' ) {
+ $query = "SELECT `parent_id`, `content_id`, `pos` FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` WHERE `pos`>? AND `parent_id`=? ORDER BY `pos` ASC";
+ } else {
+ $this->mErrors['orientation'] = tra( 'The member could not be moved since the orientation is not known.' );
+ }
+
+ // execute sql if everything is in order so far
+ if( !empty( $query ) ) {
+ $this->mDb->StartTrans();
+ $result = $this->mDb->query( $query, array( $this->mInfo['members'][$pMemberId]['pos'], $pParentId ) );
+ $res = $result->fetchRow();
+ if( $res ) {
+ //Swap positional values
+ $query = "UPDATE `".BIT_DB_PREFIX."bit_pigeonhole_members` SET `pos`=? WHERE `parent_id`=? AND `content_id`=?";
+ $this->mDb->query( $query, array( $res['pos'], $pParentId, $pMemberId ) );
+ $this->mDb->query( $query, array( $this->mInfo['members'][$pMemberId]['pos'], $res['parent_id'], $res['content_id'] ) );
+ }
+ $this->mDb->CompleteTrans();
+ }
+ } else {
+ $this->mErrors['move_member'] = tra( 'The category member could not be moved up, due to faulty data.' );
+ }
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * Store pigeonhole member
+ * @param $pParamHash an array of conent to be stored.
+ * @param $pParamHash[parent_id] id of pigeonhole it belongs to, default is $this->mContentId
+ * @param $pParamHash[content_id] content_id of the item to be stored
+ * @return bool TRUE on success, FALSE if store could not occur. If FALSE, $this->mErrors will have reason why
+ * @access public
+ **/
+ function insertPigeonholeMember( &$pParamHash ) {
+ if( $this->verifyPigeonholeMember( $pParamHash ) ) {
+ foreach( $pParamHash['member_store'] as $item ) {
+ $result = $this->mDb->associateInsert( BIT_DB_PREFIX."bit_pigeonhole_members", $item );
+ }
+ } else {
+ vd( $this->mErrors );
+ }
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * verify, clean up and prepare data to be stored
+ * @param $pParamHash all information that is being stored. will update $pParamHash by reference with fixed array of itmes
+ * @return bool TRUE on success, FALSE if store could not occur. If FALSE, $this->mErrors will have reason why
+ * @access private
+ **/
+ function verifyPigeonholeMember( &$pParamHash ) {
+ $this->mDb->StartTrans();
+ foreach( $pParamHash as $key => $item ) {
+ if( isset( $item['parent_id'] ) && $this->verifyId( $item['parent_id'] ) ) {
+ $tmp['member_store'][$key]['parent_id'] = $item['parent_id'];
+ } elseif( !empty( $this->mContentId ) ) {
+ $tmp['member_store'][$key]['parent_id'] = $this->mContentId;
+ $pParamHash[$key]['parent_id'] = $this->mContentId;
+ } else {
+ $this->mErrors['store_members'] = tra( 'The content could not be inserted because the parent_id was missing.' );
+ }
+
+ if( isset( $item['content_id'] ) && $this->verifyId( $item['content_id'] ) ) {
+ $tmp['member_store'][$key]['content_id'] = $item['content_id'];
+ } else {
+ $this->mErrors['store_members'] = 'The content id is not valid.';
+ }
+
+ // if no positional info is given, we just append the items.
+ if( isset( $item['pos'] ) && is_numeric( $item['content_id'] ) ) {
+ $tmp['member_store'][$key]['pos'] = $item['pos'];
+ } elseif( !empty( $tmp['member_store'][$key-1]['pos'] ) ) {
+ $tmp['member_store'][$key]['pos'] = $tmp['member_store'][$key-1]['pos'] + 1;
+ } else {
+ $query = "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` WHERE `parent_id`=?";
+ $tmp['member_store'][$key]['pos'] = $this->mDb->getOne( $query, array( $tmp['member_store'][$key]['parent_id'] ) ) + 1;
+ }
+ }
+
+ $this->mDb->CompleteTrans();
+ $pParamHash = $tmp;
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * Expunge pigeonhole member
+ * @param $pMemberId content_id of content to be deleted
+ * Note if only one of the 2 ids is given, all items with that id will be removed. if both are given, only that one particular entry is removed
+ * @return bool TRUE on success, FALSE if store could not occur. If FALSE, $this->mErrors will have reason why
+ * @access public
+ **/
+ function expungePigeonholeMember( $pParentId=NULL, $pMemberId=NULL ) {
+ if( !empty( $pParentId ) && $this->verifyId( $pParentId ) || !empty( $pMemberId ) && $this->verifyId( $pMemberId ) ) {
+ $where = '';
+ if( $this->verifyId( $pParentId ) ) {
+ $where .= "WHERE `parent_id`=?";
+ $bindVars[] = $pParentId;
+ }
+
+ if( $this->verifyId( $pMemberId ) ) {
+ $where .= ( empty( $where ) ? "WHERE" : "AND" )." `content_id`=?";
+ $bindVars[] = $pMemberId;
+ }
+
+ $this->mDb->StartTrans();
+ // depending on what data we've been given, we need to shift several items up to keep pos continuous
+ if( !empty( $pMemberId ) ) {
+ $query = "SELECT * FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` $where";
+ $result = $this->mDb->query( $query, $bindVars );
+ $members = $result->getRows();
+ foreach( $members as $member ) {
+ $query = "UPDATE `".BIT_DB_PREFIX."bit_pigeonhole_members` SET `pos`=`pos`-1 WHERE `pos`>? AND `parent_id`=?";
+ $this->mDb->query( $query, array( $member['pos'], $member['parent_id'] ) );
+ }
+ }
+
+ // now we're ready to remove the actual members
+ $query = "DELETE FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` $where";
+ $result = $this->mDb->query( $query, $bindVars );
+ $this->mDb->CompleteTrans();
+ } else {
+ $this->mErrors['members_store'] = 'The category member(s) could not be removed.';
+ }
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * Expunge currently loaded pigeonhole
+ * @return bool TRUE on success, FALSE if store could not occur.
+ * @access public
+ **/
+ function expunge() {
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $this->mDb->StartTrans();
+ // get all items that are part of the sub tree
+ require_once( LIBERTY_PKG_PATH.'LibertyStructure.php' );
+ $struct = new LibertyStructure();
+
+ $tree = $struct->getSubTree( $this->mStructureId );
+ foreach( $tree as $node ) {
+ $structureIds[] = $node['structure_id'];
+ }
+
+ $structureIds = array_unique( $structureIds );
+ foreach( $structureIds as $structureId ) {
+ $contentIds[] = $this->mDb->getOne( "SELECT `content_id` FROM `".BIT_DB_PREFIX."tiki_structures` WHERE `structure_id`=?", array( $structureId ) );
+ }
+
+ foreach( $contentIds as $contentId ) {
+ // now we have the content ids - let the nuking begin
+ $query = "DELETE FROM `".BIT_DB_PREFIX."bit_pigeonholes` WHERE `content_id` = ?";
+ $result = $this->mDb->query( $query, array( $contentId ) );
+ $query = "DELETE FROM `".BIT_DB_PREFIX."bit_pigeonhole_members` WHERE `parent_id` = ?";
+ $result = $this->mDb->query( $query, array( $contentId ) );
+
+ // remove all entries from content tables
+ $this->mContentId = $contentId;
+ if( LibertyAttachable::expunge() ) {
+ $ret = TRUE;
+ $this->mDb->CompleteTrans();
+ } else {
+ $this->mDb->RollbackTrans();
+ }
+ }
+
+ // finally nuke the structure in tiki_structures
+ $struct->s_remove_page( $this->mStructureId, FALSE );
+ }
+ return $ret;
+ }
+
+ /**
+ * Generates the URL to this pigeonhole
+ * @param $pContentId is the pigeonhole id we want to see
+ * @return the link to display the page.
+ */
+ function getDisplayUrl( $pContentId=NULL ) {
+ global $gBitSystem;
+ if( empty( $pContentId ) ) {
+ $pContentId = $this->mContentId;
+ }
+
+ $rewrite_tag = $gBitSystem->isFeatureActive( 'feature_pretty_urls_extended' ) ? 'view/' : '';
+ if( $gBitSystem->isFeatureActive( 'pretty_urls' ) || $gBitSystem->isFeatureActive( 'feature_pretty_urls_extended' ) ) {
+ $baseUrl = PIGEONHOLES_PKG_URL.$rewrite_tag.$pContentId;
+ } else {
+ $baseUrl = PIGEONHOLES_PKG_URL.'view.php?content_id='.$pContentId;
+ }
+
+ return $baseUrl;
+ }
+
+ /**
+ * Returns HTML link to display a pigeonhole
+ * @param $pPigeonholeTitle is the pigeonhole we want to see
+ * @return the link to display the page.
+ */
+ function getDisplayLink( $pPigeonholeTitle=NULL ) {
+ global $gBitSystem;
+ if( empty( $pPigeonholeTitle ) && !empty( $this ) ) {
+ $pPigeonholeTitle = $this->mInfo['title'];
+ }
+
+ $ret = $pPigeonholeTitle;
+ if( $gBitSystem->isPackageActive( 'pigeonholes' ) ) {
+ $ret = '<a href="'.$this->getDisplayUrl().'">'.$pPigeonholeTitle.'</a>';
+ }
+
+ return $ret;
+ }
+}
+?>
diff --git a/admin/admin_pigeonholes_inc.php b/admin/admin_pigeonholes_inc.php
new file mode 100644
index 0000000..616547f
--- /dev/null
+++ b/admin/admin_pigeonholes_inc.php
@@ -0,0 +1,41 @@
+<?php
+// $Header: /cvsroot/bitweaver/_bit_pigeonholes/admin/admin_pigeonholes_inc.php,v 1.1 2005/08/21 16:22:48 squareing Exp $
+
+$pigeonholeSettings = array(
+ 'custom_member_sorting' => array(
+ 'label' => 'Custom Sorting',
+ 'note' => 'This will change the way category members are displayed. It allows you to sort the members manually.',
+ ),
+ 'display_pigeonhole_path' => array(
+ 'label' => 'Display Path',
+ 'note' => 'Display category paths above the page leading to the object.',
+ ),
+ 'display_pigeonhole_members' => array(
+ 'label' => 'Display Members',
+ 'note' => 'Show the other members of the same categories at the bottom of the page.',
+ ),
+ 'display_pigeonhole_description' => array(
+ 'label' => 'Display Description',
+ 'note' => 'When showing the category members, you can display the category description as well.',
+ ),
+);
+$gBitSmarty->assign( 'pigeonholeSettings', $pigeonholeSettings );
+
+$memberLimit = array(
+ '9999' => 'Unlimited',
+ '10' => 10,
+ '20' => 20,
+ '30' => 30,
+ '50' => 50,
+ '100' => 100,
+);
+$gBitSmarty->assign( 'memberLimit', $memberLimit );
+
+if( !empty( $_REQUEST['pigeonhole_settings'] ) ) {
+ foreach( array_keys( $pigeonholeSettings ) as $item ) {
+ simple_set_toggle( $item, PIGEONHOLES_PKG_NAME );
+ }
+
+ simple_set_value( 'limit_member_number', PIGEONHOLES_PKG_NAME );
+}
+?>
diff --git a/admin/schema_inc.php b/admin/schema_inc.php
new file mode 100644
index 0000000..b041712
--- /dev/null
+++ b/admin/schema_inc.php
@@ -0,0 +1,54 @@
+<?php
+
+$tables = array(
+ 'bit_pigeonholes' => "
+ content_id I4 NOTNULL PRIMARY,
+ structure_id I4 NOTNULL PRIMARY
+ ",
+ 'bit_pigeonhole_members' => "
+ parent_id I4 NOTNULL PRIMARY,
+ content_id I4 NOTNULL PRIMARY,
+ pos I4 NOTNULL
+ "
+);
+
+global $gBitInstaller;
+
+foreach( array_keys( $tables ) AS $tableName ) {
+ $gBitInstaller->registerSchemaTable( PIGEONHOLES_PKG_NAME, $tableName, $tables[$tableName] );
+}
+
+$gBitInstaller->registerPackageInfo( PIGEONHOLES_PKG_NAME, array(
+ 'description' => "A Categorisation system that makes it easy to keep an overview of your data. Has a simple, yet powerful interface for categorising multiple pages at once.",
+ 'license' => '<a href="http://www.gnu.org/licenses/licenses.html#LGPL">LGPL</a>',
+ 'version' => '0.1',
+ 'state' => 'experimental',
+ 'dependencies' => '',
+) );
+
+//// ### Indexes
+//$indices = array (
+// 'bit_pigeonholes_content_idx' => array( 'table' => 'bit_pigeonholes', 'cols' => 'content_id', 'opts' => 'UNIQUE' ),
+//);
+//$gBitInstaller->registerSchemaIndexes( PIGEONHOLES_PKG_NAME, $indices );
+
+// ### Sequences
+$sequences = array (
+ 'bit_pigeonholes_id_seq' => array( 'start' => 1 )
+);
+
+$gBitInstaller->registerSchemaSequences( PIGEONHOLES_PKG_NAME, $sequences );
+
+// ### Default Preferences
+$gBitInstaller->registerPreferences( PIGEONHOLES_PKG_NAME, array(
+ array( PIGEONHOLES_PKG_NAME, 'display_pigeonhole_members','y' ),
+) );
+
+// ### Default UserPermissions
+$gBitInstaller->registerUserPermissions( PIGEONHOLES_PKG_NAME, array(
+ array( 'bit_p_view_pigeonholes', 'Can view pigeonholes', 'basic', PIGEONHOLES_PKG_NAME ),
+ array( 'bit_p_insert_pigeonhole_member', 'Can insert content into an existing pigeonhole', 'registered', PIGEONHOLES_PKG_NAME ),
+ array( 'bit_p_edit_pigeonholes', 'Can edit pigeonholes', 'editors', PIGEONHOLES_PKG_NAME ),
+) );
+
+?>
diff --git a/assign_non_members.php b/assign_non_members.php
new file mode 100644
index 0000000..18bc1ff
--- /dev/null
+++ b/assign_non_members.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * $Header: /cvsroot/bitweaver/_bit_pigeonholes/Attic/assign_non_members.php,v 1.1 2005/08/21 16:22:44 squareing Exp $
+ *
+ * Copyright ( c ) 2004 bitweaver.org
+ * Copyright ( c ) 2003 tikwiki.org
+ * Copyright ( c ) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
+ * All Rights Reserved. See copyright.txt for details and a complete list of authors.
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details
+ *
+ * $Id: assign_non_members.php,v 1.1 2005/08/21 16:22:44 squareing Exp $
+ * @package pigeonholes
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+require_once( '../bit_setup_inc.php' );
+
+$gBitSystem->verifyPackage( 'pigeonholes' );
+$gBitSystem->verifyPermission( 'bit_p_insert_pigeonhole_member' );
+
+include_once( PIGEONHOLES_PKG_PATH.'lookup_pigeonholes_inc.php' );
+
+$feedback = '';
+$gBitSmarty->assign_by_ref( 'feedback', $feedback );
+
+$contentTypes = array( '' => tra( 'All Content' ) );
+foreach( $gLibertySystem->mContentTypes as $cType ) {
+ $contentTypes[$cType['content_type_guid']] = $cType['content_description'];
+}
+$gBitSmarty->assign( 'contentTypes', $contentTypes );
+$gBitSmarty->assign( 'contentSelect', $contentSelect = !isset( $_REQUEST['content_type'] ) ? NULL : $_REQUEST['content_type'] );
+
+$listHash = array(
+ 'find' => empty( $_REQUEST['find_objects'] ) ? NULL : $_REQUEST['find_objects'],
+ 'sort_mode' => empty( $_REQUEST['sort_mode'] ) ? NULL : $_REQUEST['sort_mode'],
+ 'max_rows' => ( !empty( $_REQUEST['max_rows'] ) && is_numeric( $_REQUEST['max_rows'] ) ) ? $_REQUEST['max_rows'] : 100,
+);
+
+$nonMembers = $gPigeonholes->getNonPigeonholeMembers( $listHash, $contentSelect, ( !empty( $_REQUEST['include'] ) && $_REQUEST['include'] == 'members' ) ? $_REQUEST['include'] : FALSE );
+
+if( !empty( $_REQUEST['insert_content'] ) && isset( $_REQUEST['pigeonhole'] ) ) {
+ // make an array that can be stored
+ foreach( $nonMembers as $item ) {
+ if( !empty( $_REQUEST['pigeonhole'][$item['content_id']] ) ) {
+ foreach( $_REQUEST['pigeonhole'][$item['content_id']] as $parent_id ) {
+ $memberHash[$parent_id][] = array(
+ 'parent_id' => $parent_id,
+ 'content_id' => $item['content_id'],
+ );
+ }
+ }
+
+ if( !empty( $_REQUEST['include'] ) && $_REQUEST['include'] == 'members' ) {
+ if( !empty( $item['content_id'] ) && !$gPigeonholes->expungePigeonholeMember( NULL, $item['content_id'] ) ) {
+ $feedback['error'] = 'The content could not be deleted before insertion.';
+ }
+ }
+ }
+
+ if( empty( $feedback['error'] ) ) {
+ foreach( $memberHash as $memberStore ) {
+ if( $gPigeonholes->insertPigeonholeMember( $memberStore ) ) {
+ $feedback['success'] = 'The content was successfully inserted into the respective categories.';
+ } else {
+ $feedback['error'] = 'The content could not be inserted into the categories.';
+ }
+ }
+ }
+
+ // we need to reload the nonMembers, since settings have changed
+ $nonMembers = $gPigeonholes->getNonPigeonholeMembers( $listHash, $contentSelect, ( !empty( $_REQUEST['include'] ) && $_REQUEST['include'] == 'members' ) ? $_REQUEST['include'] : FALSE );
+}
+
+$pigeonList = $gPigeonholes->getList( NULL, FALSE, TRUE );
+$gBitSmarty->assign( 'pigeonList', $pigeonList['data'] );
+$gBitSmarty->assign( 'nonMembers', $nonMembers );
+$gBitSmarty->assign( 'contentCount', count( $nonMembers ) );
+
+// Display the template
+$gBitSystem->display( 'bitpackage:pigeonholes/assign_non_members.tpl', tra( 'Assign Content to Categories' ) );
+?>
diff --git a/bit_setup_inc.php b/bit_setup_inc.php
new file mode 100644
index 0000000..a8124f8
--- /dev/null
+++ b/bit_setup_inc.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * @author xing <xing@synapse.plus.com>
+ * @version $Revision: 1.1 $
+ * @package Pigeonholes
+ * @subpackage functions
+ */
+global $gBitSystem, $gBitUser;
+$gBitSystem->registerPackage( 'pigeonholes', dirname( __FILE__).'/' );
+
+define( 'PIGEONHOLES_CONTENT_TYPE_GUID', 'pigeonholes' );
+
+if( $gBitSystem->isPackageActive( 'pigeonholes' ) ) {
+ $gLibertySystem->registerService( LIBERTY_SERVICE_CATEGORIZATION, PIGEONHOLES_PKG_NAME, array(
+ 'content_display_function' => 'display_pigeonholes',
+ 'content_preview_function' => 'pigeonholes_preview_content',
+ 'content_edit_function' => 'pigeonholes_input_content',
+ 'content_store_function' => 'pigeonholes_store_content',
+ 'content_edit_tpl' => 'bitpackage:pigeonholes/pigeonholes_input_inc.tpl',
+ 'content_view_tpl' => 'bitpackage:pigeonholes/display_members.tpl',
+ 'content_nav_tpl' => 'bitpackage:pigeonholes/display_paths.tpl',
+ ) );
+
+ // include service functions
+ require_once( PIGEONHOLES_PKG_PATH.'servicefunctions_inc.php' );
+
+ if( $gBitUser->hasPermission( 'bit_p_view_pigeonholes' ) ) {
+ $gBitSystem->registerAppMenu( 'pigeonholes', 'Categories', PIGEONHOLES_PKG_URL.'index.php', 'bitpackage:pigeonholes/menu_pigeonholes.tpl', 'Pigeonholes' );
+ }
+}
+?>
diff --git a/edit_pigeonholes.php b/edit_pigeonholes.php
new file mode 100644
index 0000000..30fe015
--- /dev/null
+++ b/edit_pigeonholes.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * $Header: /cvsroot/bitweaver/_bit_pigeonholes/edit_pigeonholes.php,v 1.1 2005/08/21 16:22:41 squareing Exp $
+ *
+ * Copyright ( c ) 2004 bitweaver.org
+ * Copyright ( c ) 2003 tikwiki.org
+ * Copyright ( c ) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
+ * All Rights Reserved. See copyright.txt for details and a complete list of authors.
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details
+ *
+ * $Id: edit_pigeonholes.php,v 1.1 2005/08/21 16:22:41 squareing Exp $
+ * @package pigeonholes
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+require_once( '../bit_setup_inc.php' );
+
+$gBitSystem->verifyPackage( 'pigeonholes' );
+$gBitSystem->verifyPermission( 'bit_p_edit_pigeonholes' );
+
+include_once( LIBERTY_PKG_PATH.'LibertyStructure.php' );
+include_once( PIGEONHOLES_PKG_PATH.'lookup_pigeonholes_inc.php' );
+
+// include edit structure file only when structure_id is known
+if( !empty( $_REQUEST["structure_id"] ) && ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] != 'remove' ) ) {
+ $verifyStructurePermission = 'bit_p_edit_pigeonholes';
+ include_once( LIBERTY_PKG_PATH.'edit_structure_inc.php' );
+
+ // get all the nodes in this structure
+ foreach( $rootTree as $node ) {
+ $pigeonStructure[$node['structure_id']] = str_repeat( '-', $node['level'] ).' '.$node['title'];
+ }
+ $gBitSmarty->assign( 'pigeonStructure', $pigeonStructure );
+}
+
+global $gStructure;
+
+//vd($_REQUEST);
+
+// store the form if we need to
+if( !empty( $_REQUEST['pigeonhole_store'] ) ) {
+ if( ( empty( $_REQUEST['pigeonhole']['title'] ) ) ) {
+ $gBitSmarty->assign( 'msg', tra( "You must specify a title." ) );
+ $gBitSystem->display( 'error.tpl' );
+ die;
+ }
+
+ // we need to get the root structure id
+ $_REQUEST['pigeonhole']['root_structure_id'] = !empty( $rootStructure->mStructureId ) ? $rootStructure->mStructureId : NULL;
+ // store the pigeonhole
+ $pigeonStore = new Pigeonholes();
+ $pigeonStore->mContentId = !empty( $_REQUEST['content_id'] ) ? $_REQUEST['content_id'] : NULL;
+ $pigeonStore->load();
+ if( $pigeonStore->store( $_REQUEST['pigeonhole'] ) ) {
+ header( "Location: ".$_SERVER['PHP_SELF'].'?structure_id='.$pigeonStore->mStructureId );
+ } else {
+ vd( $gPigeonholes->mErrors );
+ $gBitSmarty->assign( 'msg', tra( "There was a problem trying to store the pigeonhole." ) );
+ $gBitSystem->display( 'error.tpl' );
+ die;
+ }
+}
+
+// if we are just changing the content that is being displayed, we treat it like a preview.
+if( !empty( $_REQUEST['search_objects'] ) ) {
+ $pigeonInfo['parent_id'] = !empty( $_REQUEST['pigeonhole']['parent_id'] ) ? $_REQUEST['pigeonhole']['parent_id'] : NULL;
+ $pigeonInfo['title'] = !empty( $_REQUEST['pigeonhole']['title'] ) ? $_REQUEST['pigeonhole']['title'] : NULL;
+ $pigeonInfo['data'] = !empty( $_REQUEST['pigeonhole']['edit'] ) ? $_REQUEST['pigeonhole']['edit'] : NULL;
+ $pigeonInfo['selected_members'] = !empty( $_REQUEST['pigeonhole']['members'] ) ? $_REQUEST['pigeonhole']['members'] : NULL;
+ $gBitSmarty->assign( 'pigeonInfo', !empty( $pigeonInfo ) ? $pigeonInfo : NULL );
+} elseif( !empty( $_REQUEST['action'] ) || isset( $_REQUEST["confirm"] ) ) {
+ // if we need to edit, show the information
+ if( $_REQUEST['action'] == 'edit' ) {
+ $pigeonInfo = $gPigeonholes->mInfo;
+
+ // create usable array for selected items in content listing
+ if( !empty( $pigeonInfo['members'] ) ) {
+ foreach( $pigeonInfo['members'] as $member ) {
+ if( $pigeonInfo['content_id'] == $member['parent_id'] ) {
+ $pigeonInfo['selected_members'][] = $member['content_id'];
+ }
+ }
+ }
+ }
+
+ if( $_REQUEST['action'] == 'edit' || $_REQUEST['action'] == 'create' ) {
+ $gBitSmarty->assign( 'pigeonInfo', !empty( $pigeonInfo ) ? $pigeonInfo : NULL );
+ }
+
+ if( $_REQUEST['action'] == 'move' ) {
+ $gPigeonholes->moveMember( $_REQUEST['parent_id'], $_REQUEST['member_id'], $_REQUEST['orientation'] );
+ }
+
+ if( $_REQUEST["action"] == 'remove' || isset( $_REQUEST["confirm"] ) ) {
+ if( isset( $_REQUEST["confirm"] ) ) {
+ if( $gPigeonholes->expunge( $_REQUEST["structure_id"] ) ) {
+ header( "Location: ".$_SERVER['PHP_SELF'].'?structure_id='.$gPigeonholes->mInfo["parent_id"] );
+ die;
+ } else {
+ vd( $gPigeonhole->mErrors );
+ }
+ }
+ $gBitSystem->setBrowserTitle( 'Confirm removal of '.$gPigeonholes->mInfo['title'] );
+ $formHash['remove'] = TRUE;
+ $formHash['structure_id'] = $_REQUEST['structure_id'];
+ $formHash['action'] = 'remove';
+ $msgHash = array(
+ 'label' => 'Remove Pigeonhole',
+ 'confirm_item' => $gPigeonholes->mInfo['title'].'<br />and any subitems',
+ 'warning' => 'This will remove the pigeonhole but will <strong>not</strong> modify or remove the content itself.',
+ );
+ $gBitSystem->confirmDialog( $formHash, $msgHash );
+ }
+
+ if( $_REQUEST['action'] == 'demember' && !empty( $_REQUEST['content_id'] ) && !empty( $_REQUEST['parent_id'] ) ) {
+ if( $gPigeonholes->expungePigeonholeMember( $_REQUEST['content_id'], $_REQUEST['parent_id'] ) ) {
+ $feedback['success'] = tra( 'The item was successfully removed' );
+ } else {
+ $feedback['error'] = tra( 'The item could not be removed' );
+ }
+ // used to avoid displaying edit form
+ unset( $_REQUEST['action'] );
+ }
+}
+
+// get content
+include_once( LIBERTY_PKG_PATH.'get_content_list_inc.php' );
+foreach( $contentList['data'] as $cItem ) {
+ $cList[$contentTypes[$cItem['content_type_guid']]][$cItem['content_id']] = $cItem['title'].' [id: '.$cItem['content_id'].']';
+}
+$gBitSmarty->assign( 'contentList', $cList );
+$gBitSmarty->assign( 'contentSelect', $contentSelect );
+$gBitSmarty->assign( 'contentTypes', $contentTypes );
+
+$listHash['root_structure_id'] = $gPigeonholes->mInfo['root_structure_id'];
+$pigeonList = $gPigeonholes->getList( $listHash, FALSE, TRUE );
+$gBitSmarty->assign( 'pigeonList', $pigeonList['data'] );
+
+$gBitSmarty->assign( 'feedback', !empty( $feedback ) ? $feedback : NULL );
+
+// Display the template
+$gBitSystem->display( 'bitpackage:pigeonholes/edit_pigeonholes.tpl', !empty( $gStructure ) ? tra( 'Edit Pigeonhole' ).': '.$gStructure->mInfo["title"] : tra( 'Create Pigeonhole' ) );
+?>
diff --git a/edit_structure.php b/edit_structure.php
new file mode 100644
index 0000000..51f9101
--- /dev/null
+++ b/edit_structure.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * $Header: /cvsroot/bitweaver/_bit_pigeonholes/edit_structure.php,v 1.1 2005/08/21 16:22:44 squareing Exp $
+ *
+ * Copyright ( c ) 2004 bitweaver.org
+ * Copyright ( c ) 2003 tikwiki.org
+ * Copyright ( c ) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
+ * All Rights Reserved. See copyright.txt for details and a complete list of authors.
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details
+ *
+ * $Id: edit_structure.php,v 1.1 2005/08/21 16:22:44 squareing Exp $
+ * @package pigeonholes
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+require_once( '../bit_setup_inc.php' );
+
+$gBitSystem->verifyPackage( 'pigeonholes' );
+$gBitSystem->verifyPermission( 'bit_p_edit_pigeonholes' );
+
+include_once( PIGEONHOLES_PKG_PATH.'lookup_pigeonholes_inc.php' );
+
+$verifyStructurePermission = 'bit_p_edit_pigeonholes';
+include_once( LIBERTY_PKG_PATH.'edit_structure_inc.php' );
+
+// Display the template
+$gBitSystem->display( 'bitpackage:pigeonholes/edit_structure.tpl', $gStructure->mInfo["title"] );
+?>
diff --git a/icons/organise.png b/icons/organise.png
new file mode 100644
index 0000000..8df04ad
--- /dev/null
+++ b/icons/organise.png
Binary files differ
diff --git a/icons/pkg_pigeonholes.png b/icons/pkg_pigeonholes.png
new file mode 100644
index 0000000..184e920
--- /dev/null
+++ b/icons/pkg_pigeonholes.png
Binary files differ
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..ebdf752
--- /dev/null
+++ b/index.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * $Header: /cvsroot/bitweaver/_bit_pigeonholes/index.php,v 1.1 2005/08/21 16:22:44 squareing Exp $
+ *
+ * Copyright ( c ) 2004 bitweaver.org
+ * Copyright ( c ) 2003 tikwiki.org
+ * Copyright ( c ) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
+ * All Rights Reserved. See copyright.txt for details and a complete list of authors.
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details
+ *
+ * $Id: index.php,v 1.1 2005/08/21 16:22:44 squareing Exp $
+ * @package pigeonholes
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+require_once( '../bit_setup_inc.php' );
+include_once( PIGEONHOLES_PKG_PATH.'lookup_pigeonholes_inc.php' );
+if( !empty( $gPigeonholes->mStructureId ) ) {
+ header( 'Location: '.PIGEONHOLES_PKG_URL.'view.php?structure_id='.$gPigeonholes->mStructureId );
+} else {
+ header( 'Location: '.PIGEONHOLES_PKG_URL.'list.php' );
+}
diff --git a/list.php b/list.php
new file mode 100644
index 0000000..06da14d
--- /dev/null
+++ b/list.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * $Header
+ *
+ * @author xing <xing@synapse.plus.com>
+ * @version $Revision: 1.1 $
+ * @package pigeonholes
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+require_once("../bit_setup_inc.php");
+
+$gBitSystem->verifyPackage( 'pigeonholes' );
+$gBitSystem->verifyPermission( 'bit_p_view_pigeonholes' );
+
+include_once( PIGEONHOLES_PKG_PATH.'lookup_pigeonholes_inc.php' );
+
+// some specific offsets and pagination settings
+if( !empty( $_REQUEST['sort_mode'] ) ) {
+ $gBitSmarty->assign( 'sort_mode', $_REQUEST['sort_mode'] );
+}
+
+$gBitSmarty->assign( 'curPage', $page = !empty( $_REQUEST['page'] ) ? $_REQUEST['page'] : 1 );
+$listHash = array(
+ 'sort_mode' => !empty( $_REQUEST['sort_mode'] ) ? $_REQUEST['sort_mode'] : 'title_asc',
+ 'max_rows' => $gBitSystem->mPrefs['maxRecords'],
+ 'offset' => ( $page - 1 ) * $gBitSystem->mPrefs['maxRecords'],
+ 'find' => !empty( $_REQUEST['find'] ) ? $_REQUEST['find'] : NULL,
+);
+
+$gBitSmarty->assign( 'pigeonList', $pigeonList = $gPigeonholes->getList( $listHash, TRUE, FALSE ) );
+$gBitSmarty->assign( 'numPages', ceil( $pigeonList['cant'] / $gBitSystem->mPrefs['maxRecords'] ) );
+
+// set up structure related stuff
+foreach( $pigeonList['data'] as $key => $pigeonhole ) {
+ $gStructure = new LibertyStructure( $pigeonhole['root_structure_id'] );
+ $gStructure->load();
+ $pigeonList['data'][$key]['subtree'] = $gStructure->getSubTree( $gStructure->mStructureId );
+}
+
+//$gBitSmarty->assign_by_ref('offset', $offset);
+$gBitSmarty->assign( 'pigeonList', $pigeonList['data'] );
+$gBitSmarty->assign( 'pigeonCount', $pigeonList['cant'] );
+
+$gBitSystem->display( 'bitpackage:pigeonholes/list.tpl', tra( 'List Pigeonholes' ) );
+?>
diff --git a/lookup_pigeonholes_inc.php b/lookup_pigeonholes_inc.php
new file mode 100644
index 0000000..2e14224
--- /dev/null
+++ b/lookup_pigeonholes_inc.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Provide a list of pigenoholes
+ *
+ * @package pigeonholes
+ * @subpackage functions
+ * @version $Header: /cvsroot/bitweaver/_bit_pigeonholes/lookup_pigeonholes_inc.php,v 1.1 2005/08/21 16:22:44 squareing Exp $
+ *
+ * Copyright ( c ) 2005 bitweaver.org
+ * All Rights Reserved. See copyright.txt for details and a complete list of authors.
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details
+ */
+
+/**
+ * Required Files
+ */
+require_once( PIGEONHOLES_PKG_PATH.'/Pigeonholes.php' );
+
+$gPigeonholes = new Pigeonholes( ( !empty( $_REQUEST['structure_id'] ) ? $_REQUEST['structure_id'] : NULL ), ( !empty( $_REQUEST['content_id'] ) ? $_REQUEST['content_id'] : NULL ), TRUE, TRUE );
+$gBitSmarty->assign( 'gPigeonholes', $gPigeonholes );
+?>
diff --git a/servicefunctions_inc.php b/servicefunctions_inc.php
new file mode 100644
index 0000000..6de9976
--- /dev/null
+++ b/servicefunctions_inc.php
@@ -0,0 +1,138 @@
+<?php
+/**
+ * $Header: /cvsroot/bitweaver/_bit_pigeonholes/Attic/servicefunctions_inc.php,v 1.1 2005/08/21 16:22:39 squareing Exp $
+ *
+ * Copyright ( c ) 2004 bitweaver.org
+ * All Rights Reserved. See copyright.txt for details and a complete list of authors.
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details
+ *
+ * @package pigeonholes
+ * @subpackage functions
+ */
+
+/**
+ * Pigeonhole display service
+ */
+function display_pigeonholes( &$pObject ) {
+ global $gBitSmarty, $gBitUser;
+
+ if( $gBitUser->hasPermission( 'bit_p_view_pigeonholes' ) ) {
+ require_once( PIGEONHOLES_PKG_PATH.'Pigeonholes.php' );
+ $pigeonholes = new Pigeonholes( NULL, NULL, FALSE );
+
+ if( $pigeons = $pigeonholes->getPigeonholesFromContentId( $pObject->mContentId ) ) {
+ foreach( $pigeons as $pigeon ) {
+ $pigeonholes->mContentId = $pigeon['content_id'];
+ $pigeonholes->load( TRUE );
+ $pigeonData[] = $pigeonholes->mInfo;
+ }
+ $gBitSmarty->assign( 'pigeonData', !empty( $pigeonData ) ? $pigeonData : FALSE );
+ }
+ }
+}
+
+/**
+ * Pigeonhole edit template service
+ */
+function pigeonholes_input_content( $pObject=NULL ) {
+ global $gBitSmarty, $gBitUser;
+ $pigeonPathList = array();
+
+ if( $gBitUser->hasPermission( 'bit_p_insert_pigeonhole_member' ) ) {
+ require_once( PIGEONHOLES_PKG_PATH.'Pigeonholes.php' );
+ $pigeonholes = new Pigeonholes( NULL, NULL, FALSE );
+
+ // get pigeonholes path list
+ if( $pigeonPathList = $pigeonholes->getPigeonholesPathList( !empty( $pObject->mContentId ) ? $pObject->mContentId : NULL ) ) {
+ $gBitSmarty->assign( 'pigeonPathList', $pigeonPathList );
+ }
+ }
+}
+
+/**
+ * Pigeonhole preview service
+ * when we hit preview, we make the selections persistent
+ */
+function pigeonholes_preview_content() {
+ global $gBitSmarty, $gBitUser;
+ $pigeonPathList = array();
+
+ if( $gBitUser->hasPermission( 'bit_p_insert_pigeonhole_member' ) ) {
+ require_once( PIGEONHOLES_PKG_PATH.'Pigeonholes.php' );
+ $pigeonholes = new Pigeonholes( NULL, NULL, FALSE );
+
+ // get pigeonholes path list
+ if( $pigeonPathList = $pigeonholes->getPigeonholesPathList() ) {
+ foreach( $pigeonPathList as $key => $path ) {
+ if( !empty( $_REQUEST['pigeonholes']['pigeonhole'] ) && in_array( $key, $_REQUEST['pigeonholes']['pigeonhole'] ) ) {
+ $pigeonPathList[$key][0]['selected'] = TRUE;
+ } else {
+ $pigeonPathList[$key][0]['selected'] = FALSE;
+ }
+ }
+ $gBitSmarty->assign( 'pigeonPathList', $pigeonPathList );
+ }
+ }
+}
+
+/**
+ * Pigeonhole store service
+ * store the content in any pigeonhole it wants
+ */
+function pigeonholes_store_content( $pObject, $pParamHash ) {
+ global $gBitSmarty, $gBitUser;
+ if( $gBitUser->hasPermission( 'bit_p_insert_pigeonhole_member' ) ) {
+ require_once( PIGEONHOLES_PKG_PATH.'Pigeonholes.php' );
+
+ if( !empty( $pParamHash['content_id'] ) ) {
+ if( is_object( $pObject ) && empty( $pParamHash['content_id'] ) ) {
+ $pParamHash['content_id'] = $pObject->mContentId;
+ }
+
+ $pigeonholes = new Pigeonholes( NULL, NULL, FALSE );
+ $pigeonPathList = $pigeonholes->getPigeonholesPathList( $pParamHash['content_id'] );
+
+ // here we need to work out if we need to save at all
+ // get all originally selected items
+ $selectedItem = array();
+ foreach( $pigeonPathList as $path ) {
+ if( !empty( $path[0]['selected'] ) ) {
+ $pathItem = array_pop( $path );
+ $selectedItem[] = $pathItem['content_id'];
+ }
+ }
+
+ // quick and dirty check to start off with
+ if( empty( $_REQUEST['pigeonholes'] ) || count( $_REQUEST['pigeonholes']['pigeonhole'] ) != count( $selectedItem ) ) {
+ $modified = TRUE;
+ } else {
+ // more thorough check
+ foreach( $selectedItem as $item ) {
+ if( !in_array( $item, $_REQUEST['pigeonholes']['pigeonhole'] ) ) {
+ $modified = TRUE;
+ }
+ }
+ }
+
+ if( !empty( $modified ) ) {
+ // first remove all entries with this content_id
+ if( $pigeonholes->expungePigeonholeMember( NULL, $pParamHash['content_id'] ) && !empty( $_REQUEST['pigeonholes'] ) ) {
+ // insert the content into the desired pigeonholes
+ foreach( $_REQUEST['pigeonholes']['pigeonhole'] as $p_id ) {
+ $memberHash[] = array(
+ 'parent_id' => $p_id,
+ 'content_id' => $pParamHash['content_id']
+ );
+ }
+
+ if( !$pigeonholes->insertPigeonholeMember( $memberHash ) ) {
+ $gBitSmarty->assign( 'msg', tra( "There was a problem inserting the content into the pigeonholes." ) );
+ $gBitSystem->display( 'error.tpl' );
+ die;
+ }
+ }
+ }
+ }
+ }
+}
+?>
diff --git a/templates/admin_pigeonholes.tpl b/templates/admin_pigeonholes.tpl
new file mode 100644
index 0000000..b80dfcb
--- /dev/null
+++ b/templates/admin_pigeonholes.tpl
@@ -0,0 +1,29 @@
+{* $Header: /cvsroot/bitweaver/_bit_pigeonholes/templates/admin_pigeonholes.tpl,v 1.1 2005/08/21 16:22:47 squareing Exp $ *}
+{strip}
+{form}
+ {legend legend="Category Settings"}
+ <input type="hidden" name="page" value="{$page}" />
+ {foreach from=$pigeonholeSettings key=feature item=output}
+ <div class="row">
+ {formlabel label=`$output.label` for=$feature}
+ {forminput}
+ {html_checkboxes name="$feature" values="y" checked=`$gBitSystemPrefs.$feature` labels=false id=$feature}
+ {formhelp note=`$output.note` page=`$output.page`}
+ {/forminput}
+ </div>
+ {/foreach}
+
+ <div class="row">
+ {formlabel label="Number of Members" for="member_number"}
+ {forminput}
+ {html_options name="limit_member_number" options=$memberLimit values=$memberLimit selected=`$gBitSystemPrefs.limit_member_number` id=member_number}
+ {formhelp note="Here you can specify what number of members are displayed at the bottom of a page."}
+ {/forminput}
+ </div>
+
+ <div class="row submit">
+ <input type="submit" name="pigeonhole_settings" value="{tr}Change preferences{/tr}" />
+ </div>
+ {/legend}
+{/form}
+{/strip}
diff --git a/templates/assign_non_members.tpl b/templates/assign_non_members.tpl
new file mode 100644
index 0000000..31437dc
--- /dev/null
+++ b/templates/assign_non_members.tpl
@@ -0,0 +1,112 @@
+{strip}
+<div class="edit pigeonholes">
+ <div class="header">
+ <h1>{tr}Assign Content to Categories{/tr}</h1>
+ </div>
+
+ <div class="body">
+ {if !$pigeonList}
+ {formfeedback warning="No categories have been set up yet. You need to create some before you can assign content to them."}
+ {else}
+ {form legend="Assign Content"}
+ <input type="hidden" name="sort_mode" value="{$smarty.request.sort_mode}" />
+ <div class="row">
+ {formlabel label="Restrict listing" for="content_type"}
+
+ {forminput}
+ <select name="max_rows">
+ <option>{tr}All{/tr}</option>
+ <option value="50" {if $smarty.request.max_rows eq 50}selected="selected"{/if}>{tr}50{/tr}</option>
+ <option value="100" {if $smarty.request.max_rows eq 100 or !$smarty.request.max_rows}selected="selected"{/if}>{tr}100{/tr}</option>
+ <option value="200" {if $smarty.request.max_rows eq 200}selected="selected"{/if}>{tr}200{/tr}</option>
+ <option value="500" {if $smarty.request.max_rows eq 500}selected="selected"{/if}>{tr}500{/tr}</option>
+ </select> {tr}Records{/tr}
+ {/forminput}
+
+ {forminput}
+ <select name="include">
+ <option value="">{tr}Hide assigned content{/tr}</option>
+ <option value="members" {if $smarty.request.include eq 'members'}selected="selected"{/if}>{tr}Display assigned content{/tr}</option>
+ </select>
+ {/forminput}
+
+ {forminput}
+ {html_options values=$contentTypes options=$contentTypes name=content_type id=content_type selected=$contentSelect}
+ {/forminput}
+
+ {forminput}
+ <input type="text" value="{$smarty.request.find_objects}" name="find_objects" />&nbsp;
+ {formhelp note="You can restrict the content listing to a given content type or apply a filter."}
+ {/forminput}
+ </div>
+
+ <div class="row submit">
+ <input type="submit" value="{tr}Restrict Listing{/tr}" name="search_objects" />
+ </div>
+ {/form}
+
+ {formfeedback hash=$feedback}
+
+ {form}
+ <input type="hidden" name="sort_mode" value="{$smarty.request.sort_mode}" />
+ <input type="hidden" name="include" value="{$smarty.request.include}" />
+ <input type="hidden" name="find_objects" value="{$smarty.request.find_objects}" />
+ <input type="hidden" name="content_type" value="{$contentSelect}" />
+
+ {if $gBitSystem->isFeatureActive( 'custom_member_sorting' ) && $smarty.request.include eq 'members'}
+ {formfeedback warning="Using this insertion method will reset any custom sorting you have done so far."}
+ {/if}
+
+ <table class="data">
+ <caption>{tr}Available Content{/tr} <span class="total">[ {$contentCount} ]</span></caption>
+ <tr>
+ <th>{smartlink ititle="Title" isort=title idefault=1 max_rows=$smarty.request.max_rows content_type=$contentSelect find_objects=$find_objects include=$smarty.request.include page=$page}</th>
+ <th>{smartlink ititle="Content Type" isort=content_type_guid max_rows=$smarty.request.max_rows content_type=$contentSelect find_objects=$find_objects include=$smarty.request.include page=$page}</th>
+ {if $nonMembers}
+ {foreach from=$pigeonList item=pigeon}
+ <th><abbr title="{$pigeon.title}">{counter}</abbr></th>
+ {/foreach}
+ {/if}
+ </tr>
+
+ {foreach from=$nonMembers item=item}
+ <tr class="{cycle values='odd,even'}">
+ <td><a href="{$smarty.const.BIT_ROOT_URL}index.php?content_id={$item.content_id}">{$item.title}</a></td>
+ <td>{assign var=content_type_guid value=`$item.content_type_guid`}{$contentTypes.$content_type_guid}</td>
+ {foreach from=$pigeonList item=pigeon}
+ <td style="text-align:center">
+ <input type="checkbox" name="pigeonhole[{$item.content_id}][]" value="{$pigeon.content_id}"
+ {foreach from=$item.assigned item=parent_id}
+ {if $pigeon.content_id eq $parent_id}checked="checked"{/if}
+ {/foreach}
+ title="{$pigeon.title}" />
+ </td>
+ {/foreach}
+ </tr>
+ {foreachelse}
+ <tr>
+ <td colspan="2" class="norecords">{tr}No Content can be found with your selection criteria{/tr}</td>
+ </tr>
+ {/foreach}
+ </table>
+
+ {if $nonMembers}
+ <div class="row submit">
+ <input type="submit" name="insert_content" value="Insert Content into Categories" />
+ </div>
+ {/if}
+ {/form}
+
+ {if $nonMembers}
+ {foreach from=$pigeonList item=pigeon}
+ <dl>
+ <dt>{counter name=dogEatsPigeon}</dt>
+ <dd>{$pigeon.display_path}<br /><small>{$pigeon.data|escape}</small></dd>
+ </dl>
+ {/foreach}
+ {/if}
+ {/if}
+ </div><!-- end .body -->
+</div><!-- end .liberty -->
+{/strip}
+
diff --git a/templates/display_members.tpl b/templates/display_members.tpl
new file mode 100644
index 0000000..b34d290
--- /dev/null
+++ b/templates/display_members.tpl
@@ -0,0 +1,54 @@
+{strip}
+<div class="display pigeonholes">
+ {if $gBitSystem->isFeatureActive( 'display_pigeonhole_members' ) and $pigeonData}
+ <h2>{tr}Related Items{/tr}</h2>
+ {foreach from=$pigeonData item=pigeonItem}
+ <div class="box">
+ <h3>{$pigeonItem.display_path}</h3>
+
+ <div class="boxcontent">
+ {if $pigeonItem.data and $gBitSystem->isFeatureActive( 'display_pigeonhole_description' )}
+ {$pigeonItem.data}<br />
+ {/if}
+
+ {* reset vars *}
+ {counter start=0 assign=member_count}
+ {assign var=more value=0}
+
+ {if $gBitSystem->isFeatureActive( 'custom_member_sorting' )}
+ {foreach from=$pigeonItem.members item=member}
+ {if !$gBitSystemPrefs.limit_member_number or $member_count lt $gBitSystemPrefs.limit_member_number}
+ <a href="{$smarty.const.BIT_ROOT_URL}index.php?content_id={$member.content_id}">{$member.title}</a> &bull;&nbsp;
+ {else}
+ {assign var=more value=1}
+ {/if}
+ {counter assign=member_count}
+ {/foreach}
+ {else}
+ {foreach from=$pigeonItem.members item=member}
+ {assign var=ctg1 value=$member.content_type_guid}
+
+ {if $ctg1 ne $ctg2}{if $ctg2}<br />{/if}{$gLibertySystem->mContentTypes.$ctg1.content_description}: {/if}
+
+ {if !$gBitSystemPrefs.limit_member_number or $member_count lt $gBitSystemPrefs.limit_member_number}
+ <a href="{$smarty.const.BIT_ROOT_URL}index.php?content_id={$member.content_id}">{$member.title}</a> &bull;&nbsp;
+ {else}
+ {assign var=more value=1}
+ {/if}
+
+ {counter assign=member_count}
+ {assign var=ctg2 value=$member.content_type_guid}
+ {/foreach}
+ {/if}
+
+ {if $more eq 1}
+ <a href="{$smarty.const.PIGEONHOLES_PKG_URL}view.php?structure_id={$pigeonItem.structure_id}">[ &hellip; ]</a>
+ {/if}
+ </div>
+ </div>
+ {* reset the ctg2 value *}
+ {assign var=ctg2 value=''}
+ {/foreach}
+ {/if}
+</div>
+{/strip}
diff --git a/templates/display_paths.tpl b/templates/display_paths.tpl
new file mode 100644
index 0000000..eed90a5
--- /dev/null
+++ b/templates/display_paths.tpl
@@ -0,0 +1,9 @@
+{strip}
+{if $gBitSystem->isFeatureActive( 'display_pigeonhole_path' )}
+ <div class="structurebar pigeonholesbar">
+ {foreach from=$pigeonData item=pigeonItem}
+ <span class="path">{$pigeonItem.display_path}</span>
+ {/foreach}
+ </div><!-- end .structurebar -->
+{/if}
+{/strip}
diff --git a/templates/edit_pigeonholes.tpl b/templates/edit_pigeonholes.tpl
new file mode 100644
index 0000000..ed2f2a2
--- /dev/null
+++ b/templates/edit_pigeonholes.tpl
@@ -0,0 +1,19 @@
+<div class="floaticon">{bithelp}</div>
+
+<div class="edit pigeonholes">
+ <div class="header">
+ <h1>{tr}Edit Categories{/tr}</h1>
+ </div>
+
+ <div class="body">
+ {formfeedback hash=$feedback}
+ {if $gBitUser->hasPermission( 'bit_p_edit_pigeonholes' ) and ( $smarty.request.action eq 'create' or $smarty.request.action eq 'edit' or !$gPigeonholes->mContentId )}
+ {include file="bitpackage:pigeonholes/edit_pigeonholes_inc.tpl"}
+ {elseif $gBitUser->hasPermission( 'bit_p_edit_pigeonholes' )}
+ {smartlink ititle="Insert new Category" ifile="edit_pigeonholes.php" structure_id=`$gPigeonholes->mStructureId` action=create}
+ <br />
+ {/if}
+
+ {include file="bitpackage:pigeonholes/view_structure_inc.tpl" edit=true}
+ </div><!-- end .body -->
+</div><!-- end .edit -->
diff --git a/templates/edit_pigeonholes_inc.tpl b/templates/edit_pigeonholes_inc.tpl
new file mode 100644
index 0000000..db2ce32
--- /dev/null
+++ b/templates/edit_pigeonholes_inc.tpl
@@ -0,0 +1,55 @@
+{form legend="Create / Edit Category"}
+ {if $gPigeonholes->mStructureId}
+ <input type="hidden" name="structure_id" value="{$gPigeonholes->mStructureId}" />
+ <input type="hidden" name="content_id" value="{$pigeonInfo.content_id}" />
+ <input type="hidden" name="action" value="{$smarty.request.action}" />
+
+ <div class="row">
+ {formlabel label="Parent" for="pigeonhole-parent"}
+ {forminput}
+ {* we need to disable dropdown when editing since it might confus users when nothing happens *}
+ {if $pigeonInfo.content_id}
+ {html_options id="pigeonhole-parent" name="pigeonhole[parent_id]" values=$pigeonStructure options=$pigeonStructure selected=$pigeonInfo.parent_id disabled=disabled}
+ {else}
+ {html_options id="pigeonhole-parent" name="pigeonhole[parent_id]" values=$pigeonStructure options=$pigeonStructure selected=$pigeonInfo.parent_id}
+ {/if}
+ {formhelp note="Pick where you would like to create a new sub-category. To change the hierarchy of the categories, please visit the change structure page."}
+ {/forminput}
+ </div>
+ {/if}
+
+ <div class="row">
+ {formlabel label="Title" for="pigeonhole-title"}
+ {forminput}
+ <input type="text" size="50" id="pigeonhole-title" name="pigeonhole[title]" value="{$pigeonInfo.title}" />
+ {/forminput}
+ </div>
+
+ <div class="row">
+ {formlabel label="Description" for="pigeonhole-desc"}
+ {forminput}
+ <textarea id="pigeonhole-desc" name="pigeonhole[edit]" rows="3" cols="80">{$pigeonInfo.data|escape}</textarea>
+ {formhelp note="A description of the category. This will be visible when users view this particular category."}
+ {/forminput}
+ </div>
+
+ <div class="row">
+ {formlabel label="Content" for="pigeonhole-content"}
+ {forminput}
+ {html_options values=$contentTypes options=$contentTypes name=content_type_guid selected=$contentSelect}
+ {/forminput}
+
+ {forminput}
+ {html_options multiple="multiple" size="12" name="pigeonhole[members][]" id="pigeonhole-content" values=$contentList options=$contentList selected=$pigeonInfo.selected_members}
+ {/forminput}
+
+ {forminput}
+ <input type="text" name="find_objects" value="{$smarty.request.find_objects}" />
+ <input type="submit" value="{tr}Apply filter{/tr}" name="search_objects" />
+ {/forminput}
+ </div>
+
+ <div class="row submit">
+ <input type="submit" name="pigeonhole_store" value="{tr}Save Category{/tr}" />
+ </div>
+{/form}
diff --git a/templates/edit_structure.tpl b/templates/edit_structure.tpl
new file mode 100644
index 0000000..196637f
--- /dev/null
+++ b/templates/edit_structure.tpl
@@ -0,0 +1,11 @@
+<div class="floaticon">{bithelp}</div>
+
+<div class="edit pigeonholes">
+ <div class="header">
+ <h1>{tr}Edit Category Hierarchy{/tr}</h1>
+ </div>
+
+ <div class="body">
+ {include file="bitpackage:liberty/edit_structure_inc.tpl" hide_extended=TRUE}
+ </div><!-- end .body -->
+</div><!-- end .edit -->
diff --git a/templates/list.tpl b/templates/list.tpl
new file mode 100644
index 0000000..4ac7f87
--- /dev/null
+++ b/templates/list.tpl
@@ -0,0 +1,55 @@
+{strip}
+<div class="listing pigeonholes">
+ <div class="header">
+ <h1>{tr}Categories Listing{/tr}</h1>
+ </div>
+
+ {* user sort related assigning *}
+ {if $gBitSystem->isFeatureActive( 'display_name' ) eq login}
+ {assign var=isort_author value=creator_user}
+ {assign var=isort_editor value=modifier_user}
+ {else}
+ {assign var=isort_author value=creator_real_name}
+ {assign var=isort_editor value=modifier_real_name}
+ {/if}
+
+ <div class="body">
+ {minifind}
+
+ <table class="data">
+ <caption>{tr}Available Categories{/tr} <span class="total">[ {$pigeonCount} ]</span></caption>
+ <tr>
+ <th>{smartlink ititle="Title" isort=title page=$page idefault=1} / {smartlink ititle="Description" isort=data page=$page}</th>
+ <th>{tr}Categories{/tr}</th>
+ {if $gBitUser->hasPermission( 'bit_p_edit_pigeonholes' )}
+ <th>{tr}Actions{/tr}</th>
+ {/if}
+ </tr>
+
+ {foreach from=$pigeonList item=item}
+ <tr class="{cycle values='odd,even'}">
+ <td>
+ <h2>{$item.display_link}</h2>
+ {$item.data}
+ </td>
+ <td>{include file="bitpackage:pigeonholes/view_structure_inc.tpl" plain=true subtree=$item.subtree}</td>
+ {if $gBitUser->hasPermission( 'bit_p_edit_pigeonholes' )}
+ <td class="actionicon">
+ {smartlink ititle="Insert new Category" ifile="edit_pigeonholes.php" ibiticon="liberty/new" structure_id=`$item.structure_id` action=create}
+ {smartlink ititle="Edit Category" ifile="edit_pigeonholes.php" ibiticon="liberty/edit" structure_id=`$item.structure_id`}
+ {smartlink ititle="Change Structure" ifile="edit_structure.php" ibiticon="pigeonholes/organise" structure_id=`$item.structure_id`}
+ {smartlink ititle="Remove Category" ifile="edit_pigeonholes.php" ibiticon="liberty/delete" action="remove" structure_id=`$item.structure_id`}
+ </td>
+ {/if}
+ </tr>
+ {foreachelse}
+ <tr class="norecords">
+ <td colspan="5">{tr}No Records Found{/tr}</td>
+ </tr>
+ {/foreach}
+ </table>
+
+ {libertypagination numPages=$numPages page=$curPage sort_mode=$sort_mode content_type=$contentSelect user_id=$user_id}
+ </div><!-- end .body -->
+</div><!-- end .liberty -->
+{/strip}
diff --git a/templates/menu_pigeonholes.tpl b/templates/menu_pigeonholes.tpl
new file mode 100644
index 0000000..cf96345
--- /dev/null
+++ b/templates/menu_pigeonholes.tpl
@@ -0,0 +1,26 @@
+{strip}
+<ul>
+ {if $gBitUser->hasPermission( 'bit_p_edit_pigeonholes' )}
+ <li><a class="item" href="{$smarty.const.PIGEONHOLES_PKG_URL}edit_pigeonholes.php?action=create">{biticon ipackage=liberty iname=new iexplain="Create Category"} {tr}Create Category{/tr}</a></li>
+ {/if}
+
+ {if $gBitUser->hasPermission( 'bit_p_view_pigeonholes' )}
+ <li><a class="item" href="{$smarty.const.PIGEONHOLES_PKG_URL}list.php">{biticon ipackage=liberty iname=list iexplain="List Categories"} {tr}List Categories{/tr}</a></li>
+ {if $gPigeonholes->mStructureId}
+ <li><a class="item" href="{$smarty.const.PIGEONHOLES_PKG_URL}view.php?structure_id={$gPigeonholes->mStructureId}">{biticon ipackage=liberty iname=spacer iexplain=""} {tr}View Category{/tr}</a></li>
+ {if $gBitUser->hasPermission( 'bit_p_edit_pigeonholes' ) and $gPigeonholes->mStructureId}
+ <li><a class="head" href="{$smarty.const.PIGEONHOLES_PKG_URL}edit_pigeonholes.php?structure_id={$gPigeonholes->mInfo.structure_id}">{biticon ipackage=liberty iname=edit iexplain="Edit Category"} {tr}Edit Category{/tr}</a>
+ <ul>
+ <li><a class="item" href="{$smarty.const.PIGEONHOLES_PKG_URL}edit_pigeonholes.php?structure_id={$gPigeonholes->mInfo.root_structure_id}&amp;action=create">{biticon ipackage=liberty iname=new iexplain="Insert Category"} {tr}Insert Category{/tr}</a></li>
+ <li><a class="item" href="{$smarty.const.PIGEONHOLES_PKG_URL}edit_structure.php?structure_id={$gPigeonholes->mInfo.structure_id}">{biticon ipackage=pigeonholes iname=organise iexplain="Change Structure"} {tr}Change Structure{/tr}</a></li>
+ </ul>
+ </li>
+ {/if}
+ {/if}
+ {/if}
+
+ {if $gBitUser->hasPermission( 'bit_p_insert_pigeonholes' )}
+ <li><a class="item" href="{$smarty.const.PIGEONHOLES_PKG_URL}assign_non_members.php">{biticon ipackage=liberty iname=assign iexplain="Assign Content"} {tr}Assign Content{/tr}</a></li>
+ {/if}
+</ul>
+{/strip}
diff --git a/templates/menu_pigeonholes_admin.tpl b/templates/menu_pigeonholes_admin.tpl
new file mode 100644
index 0000000..3e62101
--- /dev/null
+++ b/templates/menu_pigeonholes_admin.tpl
@@ -0,0 +1 @@
+<ul><li>{smartlink ititle="Pigeonhole Settings" ipackage="kernel" ifile="admin/index.php" page="pigeonholes"}</li></ul>
diff --git a/templates/pigeonholes_input_inc.tpl b/templates/pigeonholes_input_inc.tpl
new file mode 100644
index 0000000..2172113
--- /dev/null
+++ b/templates/pigeonholes_input_inc.tpl
@@ -0,0 +1,18 @@
+{strip}
+{if $pigeonPathList}
+ <div class="row">
+ {formlabel label="Pick Categories"}
+ {forminput}
+ {foreach from=$pigeonPathList key=pigeonId item=path}
+ <label>
+ <input type="checkbox" value="{$pigeonId}" {if $path.0.selected}checked="checked" {/if}name="pigeonholes[pigeonhole][]" />
+ {foreach from=$path item=node}
+ {if $node.parent_id} &raquo;{/if} {$node.title}
+ {/foreach}
+ <br />
+ </label>
+ {/foreach}
+ {/forminput}
+ </div>
+{/if}
+{/strip}
diff --git a/templates/structure_section_inc.tpl b/templates/structure_section_inc.tpl
new file mode 100644
index 0000000..d60f6a9
--- /dev/null
+++ b/templates/structure_section_inc.tpl
@@ -0,0 +1,97 @@
+{strip}
+{if $gPigeonholes->mStructureId eq $subtree[ix].structure_id or $smarty.request.expand_all}
+ {assign var=iname value=Expanded}
+{else}
+ {assign var=iname value=Collapsed}
+{/if}
+
+<div class="highlight">
+ {if $edit}
+ <div class="floaticon">
+ {smartlink ititle="Edit Category" ibiticon="liberty/edit" ifile="edit_pigeonholes.php" structure_id=$subtree[ix].structure_id action=edit}
+ {smartlink ititle="Remove Category" ibiticon="liberty/delete" ifile="edit_pigeonholes.php" structure_id=$subtree[ix].structure_id action=remove}
+ </div>
+ {/if}
+
+ <a href="javascript:icntoggle('sid{$subtree[ix].structure_id}');">
+ {biticon ipackage=liberty iname=$iname id=sid`$subtree[ix].structure_id`img"} {$subtree[ix].title}
+ </a> &nbsp;
+
+ <script type="text/javascript">
+ setfoldericonstate('sid{$subtree[ix].structure_id}');
+ </script>
+
+ {foreach from=$pigeonList item=pigeonItem}
+ {if $pigeonItem.structure_id eq $subtree[ix].structure_id}
+ <small> {tr}{$pigeonItem.members_count} Item(s){/tr} </small>
+ {/if}
+ {/foreach}
+
+ <noscript>
+ <div style="padding-left:18px;" class="small"><a href="{$smarty.const.PIGEONHOLES_PKG_URL}{if $edit}edit_pigeonholes{else}index{/if}.php?structure_id={$subtree[ix].structure_id}">{tr}Expand{/tr}</a></div>
+ </noscript>
+</div>
+
+{foreach from=$pigeonList item=pigeonItem}
+ {if $pigeonItem.structure_id eq $subtree[ix].structure_id}
+ <small>{$pigeonItem.data|escape}</small>
+
+ {if $pigeonItem.members}
+ <ul id="sid{$subtree[ix].structure_id}" style="display:{if $gPigeonholes->mStructureId eq $subtree[ix].structure_id or $smarty.request.expand_all}block{else}none{/if};" class="data">
+ {foreach from=$pigeonItem.members item=pigeonMember}
+ {if $gBitSystem->isFeatureActive( 'custom_member_sorting' )}
+ <li>
+ {if $edit && $gBitSystem->isFeatureActive( 'custom_member_sorting' )}
+ {if $pigeonMember.pos ne 1}
+ {smartlink ititle="Move item up" ibiticon="liberty/nav_up" expand_all=$smarty.request.expand_all ifile="edit_pigeonholes.php" action=move orientation=north structure_id=$pigeonItem.structure_id parent_id=$pigeonItem.content_id member_id=$pigeonMember.content_id}
+ {else}
+ {biticon ipackage="liberty" iname="spacer"}
+ {/if}
+
+ {if $pigeonMember.pos ne $pigeonItem.members_count}
+ {smartlink ititle="Move item down" ibiticon="liberty/nav_down" expand_all=$smarty.request.expand_all ifile="edit_pigeonholes.php" action=move orientation=south structure_id=$pigeonItem.structure_id parent_id=$pigeonItem.content_id member_id=$pigeonMember.content_id}
+ {else}
+ {biticon ipackage="liberty" iname="spacer"}
+ {/if}
+ {/if}
+ &nbsp; <a href="{$smarty.const.BIT_ROOT_URL}index.php?content_id={$pigeonMember.content_id}">{$pigeonMember.title}</a> &nbsp;
+ {if $edit}
+ {smartlink ititle="Remove Item" ibiticon="liberty/delete_small" expand_all=$smarty.request.expand_all ifile="edit_pigeonholes.php" action=demember structure_id=$pigeonItem.structure_id parent_id=$pigeonMember.content_id content_id=$pigeonItem.content_id}
+ {/if}
+ </li>
+ {else}
+ {assign var=ctg1 value=$pigeonMember.content_type_guid}
+
+ {* close off the content type <ul> *}
+ {if $ctg1 ne $ctg2 and $ctg2}
+ </ul>
+ </li>
+ {/if}
+
+ {* open the content type <ul> *}
+ {if $ctg1 ne $ctg2}
+ <li>{$gLibertySystem->mContentTypes.$ctg1.content_description}
+ <ul>
+ {/if}
+
+ <li>
+ <a href="{$smarty.const.BIT_ROOT_URL}index.php?content_id={$pigeonMember.content_id}">{$pigeonMember.title}</a>
+ {if $edit}
+ &nbsp; {smartlink ititle="Remove Item" ibiticon="liberty/delete_small" expand_all=$smarty.request.expand_all ifile="edit_pigeonholes.php" action=demember structure_id=$pigeonItem.structure_id parent_id=$pigeonMember.content_id content_id=$pigeonItem.content_id}
+ {/if}
+ </li>
+
+ {assign var=ctg2 value=$pigeonMember.content_type_guid}
+ {/if}
+ {/foreach}
+
+ {* close any open uls and lis *}
+ {if !$gBitSystem->isFeatureActive( 'custom_member_sorting' )}
+ </ul>
+ </li>
+ {/if}
+ </ul>
+ {/if}
+ {/if}
+{/foreach}
+{/strip}
diff --git a/templates/view_structure.tpl b/templates/view_structure.tpl
new file mode 100644
index 0000000..a53a3fb
--- /dev/null
+++ b/templates/view_structure.tpl
@@ -0,0 +1,15 @@
+{strip}
+<div class="display pigeonholes">
+ <div class="header">
+ <h1>{tr}Categories Listing{/tr}</h1>
+ </div>
+
+ <div class="body">
+ {if !$smarty.request.expand_all and !( $smarty.request.action eq 'edit' or $smarty.request.action eq 'create' )}
+ {smartlink ititle="Expand All" expand_all=1 structure_id=$gPigeonholes->mStructureId}
+ {/if}
+
+ {include file="bitpackage:pigeonholes/view_structure_inc.tpl"}
+ </div><!-- end .body -->
+</div><!-- end .liberty -->
+{/strip}
diff --git a/templates/view_structure_inc.tpl b/templates/view_structure_inc.tpl
new file mode 100644
index 0000000..164685a
--- /dev/null
+++ b/templates/view_structure_inc.tpl
@@ -0,0 +1,26 @@
+{strip}
+<ul class="toc">
+ <li>
+ {section name=ix loop=$subtree}
+ {assign var=structId value=$subtree[ix].structure_id}
+ {if $subtree[ix].pos eq ''}
+ {if $plain}
+ {$subtree[ix].title}
+ {else}
+ {include file="bitpackage:pigeonholes/structure_section_inc.tpl"}
+ {/if}
+ {else}
+ {if $subtree[ix].first}<ul>{else}</li>{/if}
+ {if $subtree[ix].last}</ul>{else}
+ <li>
+ {if $plain}
+ {$subtree[ix].title}
+ {else}
+ {include file="bitpackage:pigeonholes/structure_section_inc.tpl"}
+ {/if}
+ {/if}
+ {/if}
+ {/section}
+ </li>
+</ul><!-- end outermost .toc -->
+{/strip}
diff --git a/view.php b/view.php
new file mode 100644
index 0000000..898c77e
--- /dev/null
+++ b/view.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * $Header: /cvsroot/bitweaver/_bit_pigeonholes/view.php,v 1.1 2005/08/21 16:22:45 squareing Exp $
+ *
+ * Copyright ( c ) 2004 bitweaver.org
+ * Copyright ( c ) 2003 tikwiki.org
+ * Copyright ( c ) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
+ * All Rights Reserved. See copyright.txt for details and a complete list of authors.
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details
+ *
+ * $Id: view.php,v 1.1 2005/08/21 16:22:45 squareing Exp $
+ * @package pigeonholes
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+require_once( '../bit_setup_inc.php' );
+
+$gBitSystem->verifyPackage( 'pigeonholes' );
+$gBitSystem->verifyPermission( 'bit_p_view_pigeonholes' );
+
+include_once( PIGEONHOLES_PKG_PATH.'lookup_pigeonholes_inc.php' );
+
+// set up structure related stuff
+global $gStructure;
+$gStructure = new LibertyStructure( $gPigeonholes->mInfo['root_structure_id'] );
+$gStructure->load();
+
+// order matters for these conditionals
+if( empty( $gStructure ) || !$gStructure->isValid() ) {
+ $gBitSystem->fatalError( 'Invalid structure' );
+}
+
+$gBitSmarty->assign_by_ref( 'gStructure', $gStructure );
+$gBitSmarty->assign( 'structureInfo', $gStructure->mInfo );
+$gBitSmarty->assign( 'subtree', $gStructure->getSubTree( $gStructure->mStructureId ) );
+
+$listHash = array( 'root_structure_id' => $gPigeonholes->mInfo['root_structure_id'] );
+$pigeonList = $gPigeonholes->getList( $listHash, FALSE, TRUE );
+$gBitSmarty->assign( 'pigeonList', $pigeonList['data'] );
+
+// Display the template
+$gBitSystem->display( 'bitpackage:pigeonholes/view_structure.tpl', tra( 'View Pigeonhole' ) );
+?>