SetInitialValues(); $this->SetProperties(); $this->mReadyForEdit = false; } /** * Sets object to some sane initial values * * @internal */ protected function SetInitialValues() { $this->mType = strtolower(get_class($this)) ; } /** * Subclasses should override this to set their property types using a lot * of mProperties.Add statements */ protected function SetProperties() { $this->AddBaseProperty('title',1,1); $this->AddBaseProperty('menutext',2,1); $this->AddBaseProperty('alias',5); $this->AddBaseProperty('page_url',6); $this->AddBaseProperty('parent',7,1); $this->AddBaseProperty('active',8); $this->AddBaseProperty('showinmenu',9); $this->AddBaseProperty('secure',10); $this->AddBaseProperty('cachable',11); $this->AddContentProperty('target',12); $this->AddContentProperty('image',50); $this->AddContentProperty('thumbnail',50); $this->AddBaseProperty('titleattribute',55); $this->AddBaseProperty('accesskey',56); $this->AddBaseProperty('tabindex',57); $this->AddContentProperty('extra1',80); $this->AddContentProperty('extra2',81); $this->AddContentProperty('extra3',82); $this->AddBaseProperty('owner',90); $this->AddBaseProperty('additionaleditors',91); } /************************************************************************/ /* Functions giving access to needed elements of the content */ /************************************************************************/ public function __clone() { $this->mId = -1; $this->mItemOrder = -1; $this->mOldItemOrder = -1; $this->mURL = ''; $this->mAlias = ''; } /** * Returns the ID */ public function Id() { return $this->mId; } /** * Set the numeric id of the content item * * @param integer Integer id * @access private * @internal */ public function SetId($id) { if( $id <= 0 ) return; $this->mId = $id; $this->DoReadyForEdit(); } /** * Returns a friendly name for this content type * * @abstract * @return string */ public function FriendlyName() { return ''; } /** * Returns the Name * * @return string */ public function Name() { return $this->mName; } /** * Set the the page name * * @param string The name. */ public function SetName($name) { $this->DoReadyForEdit(); $this->mName = $name; } /** * Returns the Alias * * @return string */ public function Alias() { return $this->mAlias; } /** * Returns the Type * * @return string */ public function Type() { return strtolower($this->mType); } /** * Set the page type * * @param string type * @abstract * @internal */ protected function SetType($type) { $this->DoReadyForEdit(); $this->mType = strtolower($type); } /** * Returns the Owner * * @return integer */ public function Owner() { return $this->mOwner; } /** * Set the page owner. * No validation is performed. * * @param integer Owner's user id */ public function SetOwner($owner) { if( $owner <= 0 ) return; $this->DoReadyForEdit(); $this->mOwner = $owner; } /** * Returns the Metadata * * @return string */ public function Metadata() { return $this->mMetadata; } /** * Content object handles the alias * * @return boolean */ public function HandlesAlias() { return false; } /** * Set the page metadata * * @param string The metadata */ public function SetMetadata($metadata) { $this->DoReadyForEdit(); $this->mMetadata = $metadata; } /** * Return the page tabindex value * * @return integer */ public function TabIndex() { return $this->mTabIndex; } /** * Set the page tabindex value * * @param integer tabindex */ public function SetTabIndex($tabindex) { $this->DoReadyForEdit(); $this->mTabIndex = $tabindex; } /** * Return the page title attribute * * @return string */ public function TitleAttribute() { return $this->mTitleAttribute; } /** * Retrieve the creation date of this content object. * * @return int Unix Timestamp of the creation date */ public function GetCreationDate() { return strtotime($this->mCreationDate); } /** * Retrieve the date of the last modification of this content object. * * @return int Unix Timestamp of the modification date. */ public function GetModifiedDate() { return strtotime($this->mModifiedDate); } /** * Set the title attribute of the page * * @param string The title attribute */ public function SetTitleAttribute($titleattribute) { $this->DoReadyForEdit(); $this->mTitleAttribute = $titleattribute; } /** * Get the access key (for accessibility) for this page. * * @return string */ public function AccessKey() { return $this->mAccessKey; } /** * Set the access key (for accessibility) for this page * * @param string Access Key */ public function SetAccessKey($accesskey) { $this->DoReadyForEdit(); $this->mAccessKey = $accesskey; } /** * Returns the id of this pages parent. * * @return int -1 if this page has no parent. Positive integer otherwise. */ public function ParentId() { return $this->mParentId; } /** * Sets the parent of this page. * * @param int The numeric page parent id. Use -1 for no parent. */ public function SetParentId($parentid) { $this->DoReadyForEdit(); $this->mParentId = $parentid; } /** * Retrieve the 'old parent id' for this page. * This may return a value if the page's parent has changed or the page has been copied. * * @deprecated * @internal * @return integer */ public function OldParentId() { return $this->mOldParentId; } /** * Sets the OldParentId value for this page * This may be necessary when changin page ownership or copying a page * * @deprecated * @internal * @param int ParentID ... -1 indicates no parent. */ public function SetOldParentId($parentid) { $this->DoReadyForEdit(); $this->mOldParentId = $parentid; } /** * Return the id of the page template associated with this content page. * * @return integer. */ public function TemplateId() { return $this->mTemplateId; } /** * Set the id of the page template associated with this content page. * * @param integer */ public function SetTemplateId($templateid) { $this->DoReadyForEdit(); $this->mTemplateId = $templateid; } /** * Returns the ItemOrder * The ItemOrder is used to specify the order of this page within the parent. * * @return int */ public function ItemOrder() { return $this->mItemOrder; } /** * Sets the ItemOrder * The ItemOrder is used to specify the order of this page within the parent. * * @internal * @param int the itemorder. */ public function SetItemOrder($itemorder) { $this->DoReadyForEdit(); $this->mItemOrder = $itemorder; } /** * Return the old item order. * * @deprecated * @internal * @return int */ public function OldItemOrder() { return $this->mOldItemOrder; } /** * Sets the old item order. * * @deprecated * @internal * @param int The item order. */ public function SetOldItemOrder($itemorder) { $this->DoReadyForEdit(); $this->mOldItemOrder = $itemorder; } /** * Returns the Hierarchy * * @return string */ public function Hierarchy() { $gCms = cmsms(); $contentops = $gCms->GetContentOperations(); return $contentops->CreateFriendlyHierarchyPosition($this->mHierarchy); } /** * Sets the Hierarchy * * @internal * @param string */ public function SetHierarchy($hierarchy) { $this->DoReadyForEdit(); $this->mHierarchy = $hierarchy; } /** * Returns the Id Hierarchy * * @return string */ public function IdHierarchy() { return $this->mIdHierarchy; } /** * Sets the Id Hierarchy * * @internal * @param string */ public function SetIdHierarchy($idhierarchy) { $this->DoReadyForEdit(); $this->mIdHierarchy = $idhierarchy; } /** * Returns the Hierarchy Path * * @return string */ public function HierarchyPath() { return $this->mHierarchyPath; } /** * Sets the Hierarchy Path * * @internal * @param string */ public function SetHierarchyPath($hierarchypath) { $this->DoReadyForEdit(); $this->mHierarchyPath = $hierarchypath; } /** * Returns the Active state * * @return boolean */ public function Active() { return $this->mActive; } /** * Sets this page as active * * @param boolean */ public function SetActive($active) { $this->DoReadyForEdit(); $this->mActive = $active; } /** * Returns whether preview should be available for this content type * * @abstract * @return boolean */ public function HasPreview() { return (bool) $this->mPreview; } /** * Returns whether it should show in the menu * * @return boolean */ public function ShowInMenu() { return $this->mShowInMenu; } /** * Sets whether this page should be shown in menus * * @param boolean */ public function SetShowInMenu($showinmenu) { $this->DoReadyForEdit(); $this->mShowInMenu = $showinmenu; } /** * Returns if the page is the default * * @return boolean */ public function DefaultContent() { return $this->mDefaultContent; } /** * Sets if this page should be considered the default * Note: does not modify the flags for any other content page. * * @param boolean */ public function SetDefaultContent($defaultcontent) { $this->DoReadyForEdit(); $this->mDefaultContent = $defaultcontent; } /** * Return wether this page is cachable * * @return boolean */ public function Cachable() { return $this->mCachable; } /** * Set wether this page is cachable * * @param boolean */ public function SetCachable($cachable) { $this->DoReadyForEdit(); $this->mCachable = $cachable; } /** * Return wether this page should be accessed via a secure protocol. * The secure flag effectsw wether the ssl protocol and appropriate config entries are used when generating urls to this page. * * @return boolean */ public function Secure() { return $this->mSecure; } /** * Set wether this page should be accessed via a secure protocol. * The secure flag effectsw wether the ssl protocol and appropriate config entries are used when generating urls to this page. * * @return boolean */ public function SetSecure($secure) { $this->DoReadyForEdit(); $this->mSecure = $secure; } /** * Return the page url (if any) associated with this content page. * Note: some content types do not support page urls. * * @return string */ public function URL() { return $this->mURL; } /** * Set the page url (if any) associated with this content page. * Note: some content types do not support page urls. * The url should be relative to the root url. i.e: /some/path/to/the/page * * @param string */ public function SetURL($url) { $this->DoReadyForEdit(); $this->mURL = $url; } /** * Return the markup for this page. usually xhtml or html. * * @deprecated * @return string */ public function Markup() { return $this->mMarkup; } /** * Set the markup for this page. usually xhtml or html. * * @deprecated * @param string */ public function SetMarkup($markup) { $this->DoReadyForEdit(); $this->mMarkup = $markup; } /** * Return the last modified date for this item * This is usually set on save. * * @return Date */ public function LastModifiedBy() { return $this->mLastModifiedBy; } /** * Set the last modified date for this item * This is usually set on save. * * @param Date */ public function SetLastModifiedBy($lastmodifiedby) { $this->DoReadyForEdit(); $this->mLastModifiedBy = $lastmodifiedby; } /** * Indicates wether this content type requires an alias * * @abstract * @return boolean */ public function RequiresAlias() { return TRUE; } /** * Indicates wether this content type is viewable (i.e: can be rendered) * some content types (like redirection links) are not viewable. * * @abstract * @return boolean */ public function IsViewable() { return TRUE; } /** * Set the page alias for this content page. * If an empty alias is supplied, and depending upon the doAutoAliasIfEnabled flag, and config entries * a suitable alias may be calculated from other data in the page object * This method relies on the menutext and the name of the content page already being set. * * @param string The alias * @param boolean Wether an alias should be calculated or not. */ public function SetAlias($alias, $doAutoAliasIfEnabled = true) { $this->DoReadyForEdit(); $gCms = cmsms(); $config = $gCms->GetConfig(); $tolower = false; if ($alias == '' && $doAutoAliasIfEnabled && $config['auto_alias_content'] == true) { $alias = trim($this->mMenuText); if ($alias == '') { $alias = trim($this->mName); } $tolower = true; $alias = munge_string_to_url($alias, $tolower); // Make sure auto-generated new alias is not already in use on a different page, if it does, add "-2" to the alias $contentops = $gCms->GetContentOperations(); $error = $contentops->CheckAliasError($alias, $this->Id()); if ($error !== FALSE) { if (FALSE == empty($alias)) { $alias_num_add = 2; // If a '-2' version of the alias already exists // Check the '-3' version etc. while ($contentops->CheckAliasError($alias.'-'.$alias_num_add) !== FALSE) { $alias_num_add++; } $alias .= '-'.$alias_num_add; } else { $alias = ''; } } } $this->mAlias = munge_string_to_url($alias, $tolower); } /** * Returns the menu text for this content page * * @return string */ public function MenuText() { return $this->mMenuText; } /** * Sets the menu text for this content page * * @param string */ public function SetMenuText($menutext) { $this->DoReadyForEdit(); $this->mMenuText = $menutext; } /** * Returns number of immediate child-content items of this content * * @return integer */ public function ChildCount() { $hm = cmsms()->GetHierarchyManager(); $node = $hm->getNodeById($this->mId); if( $node ) { return $node->count_children(); } } /** * Returns the properties * * @return array */ public function Properties() { return $this->_props; } /** * Test wether this content page has the named property * Properties will be loaded from the database if necessary. * * @return boolean */ public function HasProperty($name) { if( !is_array($this->_props) ) { $this->_load_properties(); } if( !is_array($this->_props) ) { return FALSE; } return in_array($name,array_keys($this->_props)); } /** * Get the value for the named property * * @return mixed String value, or null if the property does not exist. */ public function GetPropertyValue($name) { if( $this->HasProperty($name) ) { return $this->_props[$name]; } } private function _load_properties() { if( $this->mId <= 0 ) return FALSE; $this->_props = array(); $db = cmsms()->GetDb(); $query = 'SELECT * FROM '.cms_db_prefix().'content_props WHERE content_id = ?'; $dbr = $db->Execute($query,array($this->mId)); while( $dbr && !$dbr->EOF ) { $row = $dbr->fields; $this->_props[$row['prop_name']] = $row['content']; $dbr->MoveNext(); } return TRUE; } private function _save_properties() { if( $this->mId <= 0 ) return FALSE; if( !is_array($this->_props) || count($this->_props) == 0 ) return FALSE; $db = cmsms()->GetDb(); $query = 'SELECT prop_name FROM '.cms_db_prefix().'content_props WHERE content_id = ?'; $gotprops = $db->GetCol($query,array($this->mId)); $now = $db->DbTimeStamp(time()); $iquery = 'INSERT INTO '.cms_db_prefix()."content_props (content_id,type,prop_name,content,modified_date) VALUES (?,?,?,?,$now)"; $uquery = 'UPDATE '.cms_db_prefix()."content_props SET content = ?, modified_date = $now WHERE content_id = ? AND prop_name = ?"; foreach( $this->_props as $key => $value ) { if( in_array($key,$gotprops) ) { // update $dbr = $db->Execute($uquery,array($value,$this->mId,$key)); } else { // insert $dbr = $db->Execute($iquery,array($this->mId,'string',$key,$value)); } } return TRUE; } /** * Set the value of a the named property. * This method will load properties for this content page if necessary. * * @param string The property name * @param string The property value. */ public function SetPropertyValue($name, $value) { if( !is_array($this->_props) ) $this->_props = array(); $this->_props[$name] = $value; if( !is_array($this->_props) ) $this->_load_properties(); $this->_props[$name] = $value; } /** * Set the value of a the named property. * This method will not load properties * * @param string The property name * @param string The property value. */ public function SetPropertyValueNoLoad($name,$value) { if( !is_array($this->_props) ) $this->_props = array(); $this->_props[$name] = $value; } /** * Function content types to use to say whether or not they should show * up in lists where parents of content are set. This will default to true, * but should be used in cases like Separator where you don't want it to * have any children. * * @since 0.11 * @return boolean */ public function WantsChildren() { return true; } /** * Should this link be used in various places where a link is the only * useful output? (Like next/previous links in cms_selflink, for example) * * @abstract * @return boolean */ public function HasUsableLink() { return true; } /** * Is this content type copyable ? * * @abstract * @return boolean */ public function IsCopyable() { return FALSE; } /** * Indicates wether this is a system page type. * * @return boolean */ public function IsSystemPage() { return FALSE; } /************************************************************************/ /* The rest */ /************************************************************************/ /** * This is a callback function to handle any things that might need to be done before the content page is edited. * edited. * * @abstract * @return void */ public function ReadyForEdit() { } /** * A method that sets the content page into a 'ready for edit stage'; * * @return void */ protected function DoReadyForEdit() { if ($this->mReadyForEdit == false) { $this->ReadyForEdit(); $this->mReadyForEdit = true; } } /** * Load the content of the object from an ID * This method modifies the current object. * * @param $id the ID of the element * @param $loadProperties whether to load or not the properties * * @returns bool If it fails, the object comes back to initial values and returns FALSE * If everything goes well, it returns TRUE */ public function LoadFromId($id, $loadProperties = false) { $gCms = cmsms(); global $debug_errors; $db = $gCms->GetDb(); $config = $gCms->GetConfig(); $result = false; if (-1 < $id) { $query = "SELECT * FROM ".cms_db_prefix()."content WHERE content_id = ?"; $row = $db->Execute($query, array($id)); if ($row && !$row->EOF) { $this->mId = $row->fields["content_id"]; $this->mName = $row->fields["content_name"]; $this->mAlias = $row->fields["content_alias"]; $this->mOldAlias = $row->fields["content_alias"]; $this->mType = strtolower($row->fields["type"]); $this->mOwner = $row->fields["owner_id"]; $this->mParentId = $row->fields["parent_id"]; $this->mOldParentId = $row->fields["parent_id"]; $this->mTemplateId = $row->fields["template_id"]; $this->mItemOrder = $row->fields["item_order"]; $this->mOldItemOrder = $row->fields["item_order"]; $this->mMetadata = $row->fields['metadata']; $this->mHierarchy = $row->fields["hierarchy"]; $this->mIdHierarchy = $row->fields["id_hierarchy"]; $this->mHierarchyPath = $row->fields["hierarchy_path"]; $this->mMenuText = $row->fields['menu_text']; $this->mMarkup = $row->fields['markup']; $this->mTitleAttribute = $row->fields['titleattribute']; $this->mAccessKey = $row->fields['accesskey']; $this->mTabIndex = $row->fields['tabindex']; $this->mActive = ($row->fields["active"] == 1 ? true : false); $this->mDefaultContent = ($row->fields["default_content"] == 1 ? true : false); $this->mShowInMenu = ($row->fields["show_in_menu"] == 1 ? true : false); $this->mCachable = ($row->fields["cachable"] == 1 ? true : false); $this->mSecure = $row->fields['secure']; $this->mURL = $row->fields['page_url']; $this->mLastModifiedBy = $row->fields["last_modified_by"]; $this->mCreationDate = $row->fields["create_date"]; $this->mModifiedDate = $row->fields["modified_date"]; $result = true; } else { if (true == $config["debug"]) { # :TODO: Translate the error message $debug_errors .= "

Could not retrieve content from db

\n"; } } if ($row) $row->Close(); if ($result && $loadProperties) { if( !is_array($this->_props) ) $this->_load_properties(); if (!is_array($this->_props) ) { $result = false; // debug mode if (true == $config["debug"]) { # :TODO: Translate the error message $debug_errors .= "

Could not load properties for content

\n"; } } } if (false == $result) { $this->SetInitialValues(); } } else { // debug mode if ($config["debug"] == true) { # :TODO: Translate the error message $debug_errors .= "

The id wasn't valid : $id

\n"; } } $this->Load(); return $result; } /** * Load the content of the object from an array * This method modifies the current object. * * There is no check on the data provided, because this is the job of * ValidateData * * @returns bool If it fails, the object comes back to initial values and returns FALSE * If everything goes well, it returns TRUE */ function LoadFromData(&$data, $loadProperties = false) { $result = true; $this->mId = $data["content_id"]; $this->mName = $data["content_name"]; $this->mAlias = $data["content_alias"]; $this->mOldAlias = $data["content_alias"]; $this->mType = strtolower($data["type"]); $this->mOwner = $data["owner_id"]; $this->mParentId = $data["parent_id"]; $this->mOldParentId = $data["parent_id"]; $this->mTemplateId = $data["template_id"]; $this->mItemOrder = $data["item_order"]; $this->mOldItemOrder = $data["item_order"]; $this->mMetadata = $data['metadata']; $this->mHierarchy = $data["hierarchy"]; $this->mIdHierarchy = $data["id_hierarchy"]; $this->mHierarchyPath = $data["hierarchy_path"]; $this->mMenuText = $data['menu_text']; $this->mMarkup = $data['markup']; $this->mTitleAttribute = $data['titleattribute']; $this->mAccessKey = $data['accesskey']; $this->mTabIndex = $data['tabindex']; $this->mDefaultContent = ($data["default_content"] == 1 ? true : false); $this->mActive = ($data["active"] == 1 ? true : false); $this->mShowInMenu = ($data["show_in_menu"] == 1 ? true : false); $this->mCachable = ($data["cachable"] == 1 ? true : false); if( isset($data['secure']) ) $this->mSecure = $data["secure"]; if( isset($data['page_url']) ) $this->mURL = $data["page_url"]; $this->mLastModifiedBy = $data["last_modified_by"]; $this->mCreationDate = $data["create_date"]; $this->mModifiedDate = $data["modified_date"]; if ($loadProperties == true) { $this->_load_properties(); if (!is_array($this->_props) ) { $result = false; global $debug_errors; $gCms = cmsms(); $config = $gCms->GetConfig(); // debug mode if (true == $config["debug"]) { # :TODO: Translate the error message $debug_errors .= "

Could not load properties for content

\n"; } } } if (false == $result) { $this->SetInitialValues(); } $this->Load(); return $result; } /** * Callback function for content types to use to preload content or other things if necessary. This * is called right after the content is loaded from the database. * */ protected function Load() { } /** * Save or update the content * * @todo This function should return something (or throw an exception) */ function Save() { Events::SendEvent('Core', 'ContentEditPre', array('content' => &$this)); if( !is_array($this->_props) ) { debug_buffer('save is loading properties'); $this->_load_properties(); } if (-1 < $this->mId) { $this->Update(); } else { $this->Insert(); } Events::SendEvent('Core', 'ContentEditPost', array('content' => &$this)); } /** * Update the content * We can notice, that only a few things are updated * We do not care about hierarchy for example. This is because hierarchy, * order or parents management is the job of the content manager. * Remember that a content is like a file, and a file don't know where it is * on the disk, it only knows its own content. It's the same here. * * @todo this function should return something, or throw an exception. */ protected function Update() { $gCms = cmsms(); global $debug_errors; $db = $gCms->GetDb(); $config = $gCms->GetConfig(); $result = false; #Figure out the item_order (if necessary) if ($this->mItemOrder < 1) { $query = "SELECT ".$db->IfNull('max(item_order)','0')." as new_order FROM ".cms_db_prefix()."content WHERE parent_id = ?"; $row = $db->GetRow($query,array($this->mParentId)); if ($row) { if ($row['new_order'] < 1) { $this->mItemOrder = 1; } else { $this->mItemOrder = $row['new_order'] + 1; } } } $this->mModifiedDate = trim($db->DBTimeStamp(time()), "'"); $query = "UPDATE ".cms_db_prefix()."content SET content_name = ?, owner_id = ?, type = ?, template_id = ?, parent_id = ?, active = ?, default_content = ?, show_in_menu = ?, cachable = ?, secure = ?, page_url = ?, menu_text = ?, content_alias = ?, metadata = ?, titleattribute = ?, accesskey = ?, tabindex = ?, modified_date = ?, item_order = ?, markup = ?, last_modified_by = ? WHERE content_id = ?"; $dbresult = $db->Execute($query, array( $this->mName, $this->mOwner, strtolower($this->mType), $this->mTemplateId, $this->mParentId, ($this->mActive == true ? 1 : 0), ($this->mDefaultContent == true ? 1 : 0), ($this->mShowInMenu == true ? 1 : 0), ($this->mCachable == true ? 1 : 0), $this->mSecure, $this->mURL, $this->mMenuText, $this->mAlias, $this->mMetadata, $this->mTitleAttribute, $this->mAccessKey, $this->mTabIndex, $this->mModifiedDate, $this->mItemOrder, $this->mMarkup, $this->mLastModifiedBy, $this->mId )); if (!$dbresult) { if (true == $config["debug"]) { # :TODO: Translate the error message $debug_errors .= "

Error updating content

\n"; } } if ($this->mOldParentId != $this->mParentId) { #Fix the item_order if necessary $query = "UPDATE ".cms_db_prefix()."content SET item_order = item_order - 1 WHERE parent_id = ? AND item_order > ?"; $result = $db->Execute($query, array($this->mOldParentId,$this->mOldItemOrder)); $this->mOldParentId = $this->mParentId; $this->mOldItemOrder = $this->mItemOrder; } if (isset($this->mAdditionalEditors)) { $query = "DELETE FROM ".cms_db_prefix()."additional_users WHERE content_id = ?"; $db->Execute($query, array($this->Id())); foreach ($this->mAdditionalEditors as $oneeditor) { $new_addt_id = $db->GenID(cms_db_prefix()."additional_users_seq"); $query = "INSERT INTO ".cms_db_prefix()."additional_users (additional_users_id, user_id, content_id) VALUES (?,?,?)"; $db->Execute($query, array($new_addt_id, $oneeditor, $this->Id())); } } if( is_array($this->_props) && count($this->_props) ) { // :TODO: There might be some error checking there $this->_save_properties(); } else { if (true == $config["debug"]) { # :TODO: Translate the error message $debug_errors .= "

Error updating : the content has no properties

\n"; } } cms_route_manager::del_static('','__CONTENT__',$this->mId); if( $this->mURL != '' ) { $route = CmsRoute::new_builder($this->mURL,'__CONTENT__',$this->mId,null,TRUE);; cms_route_manager::add_static($route); } } /** * Insert the content in the db * * @todo this function should return something */ # :TODO: This function should return something # :TODO: Take care bout hierarchy here, it has no value ! # :TODO: Figure out proper item_order protected function Insert() { $gCms = cmsms(); global $debug_errors; $db = $gCms->GetDb(); $config = $gCms->GetConfig(); $result = false; #Figure out the item_order if ($this->mItemOrder < 1) { $query = "SELECT max(item_order) as new_order FROM ".cms_db_prefix()."content WHERE parent_id = ?"; $row = $db->Getrow($query, array($this->mParentId)); if ($row) { if ($row['new_order'] < 1) { $this->mItemOrder = 1; } else { $this->mItemOrder = $row['new_order'] + 1; } } } $newid = $db->GenID(cms_db_prefix()."content_seq"); $this->mId = $newid; $this->mModifiedDate = $this->mCreationDate = trim($db->DBTimeStamp(time()), "'"); $query = "INSERT INTO ".$config["db_prefix"]."content (content_id, content_name, content_alias, type, owner_id, parent_id, template_id, item_order, hierarchy, id_hierarchy, active, default_content, show_in_menu, cachable, secure, page_url, menu_text, markup, metadata, titleattribute, accesskey, tabindex, last_modified_by, create_date, modified_date) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; $dbresult = $db->Execute($query, array( $newid, $this->mName, $this->mAlias, strtolower($this->mType), $this->mOwner, $this->mParentId, $this->mTemplateId, $this->mItemOrder, $this->mHierarchy, $this->mIdHierarchy, ($this->mActive == true ? 1 : 0), ($this->mDefaultContent == true ? 1 : 0), ($this->mShowInMenu == true ? 1 : 0), ($this->mCachable == true ? 1 : 0), $this->mSecure, $this->mURL, $this->mMenuText, $this->mMarkup, $this->mMetadata, $this->mTitleAttribute, $this->mAccessKey, $this->mTabIndex, $this->mLastModifiedBy, $this->mModifiedDate, $this->mCreationDate )); if (! $dbresult) { die($db->sql.'
'.$db->ErrorMsg()); if ($config["debug"] == true) { # :TODO: Translate the error message $debug_errors .= "

Error inserting content

\n"; } } if (is_array($this->_props) && count($this->_props)) { // :TODO: There might be some error checking there debug_buffer('save from ' . __LINE__); $this->_save_properties(); } else { if (true == $config["debug"]) { # :TODO: Translate the error message $debug_errors .= "

Error inserting : the content has no properties

\n"; } } if (isset($this->mAdditionalEditors)) { foreach ($this->mAdditionalEditors as $oneeditor) { $new_addt_id = $db->GenID(cms_db_prefix()."additional_users_seq"); $query = "INSERT INTO ".cms_db_prefix()."additional_users (additional_users_id, user_id, content_id) VALUES (?,?,?)"; $db->Execute($query, array($new_addt_id, $oneeditor, $this->Id())); } } if( $this->mURL != '' ) { $route = CmsRoute::new_builder($this->mURL,'__CONTENT__',$this->mId,'',TRUE); cms_route_manager::add_static($route); } } /** * Test if the array given contains valid data for the object * This function is used to check that no compulsory argument * has been forgotten by the user * * We do not check the Id because there can be no Id (new content) * That's up to Save to check this. * * @returns FALSE if data is ok, and an array of invalid parameters else */ public function ValidateData() { $errors = array(); if ($this->mParentId < -1) { $errors[] = lang('invalidparent'); $result = false; } if ($this->mName == '') { if ($this->mMenuText != '') { $this->mName = $this->mMenuText; } else { $errors[]= lang('nofieldgiven',array(lang('title'))); $result = false; } } if ($this->mMenuText == '') { if ($this->mName != '') { $this->mMenuText = $this->mName; } else { $errors[]=lang('nofieldgiven',array(lang('menutext'))); $result = false; } } if (!$this->HandlesAlias()) { if ($this->mAlias != $this->mOldAlias || ($this->mAlias == '' && $this->RequiresAlias()) ) { $gCms = cmsms(); $contentops = $gCms->GetContentOperations(); $error = $contentops->CheckAliasError($this->mAlias, $this->mId); if ($error !== FALSE) { $errors[]= $error; $result = false; } } } $auto_type = content_assistant::auto_create_url(); if( $this->mURL == '' && get_site_preference('content_autocreate_urls') ) { // create a valid url. if( !$this->DefaultContent() ) { if( get_site_preference('content_autocreate_flaturls',0) ) { // the default url is the alias... but not synced to the alias. $this->mURL = $this->mAlias; } else { // if it don't explicitly say 'flat' we're creating a hierarchical url. $gCms = cmsms(); $tree = $gCms->GetHierarchyManager(); $node = $tree->find_by_tag('id',$this->ParentId()); $stack = array($this->mAlias); $parent_url = ''; $count = 0; while( $node ) { $tmp_content = $node->GetContent(); if( $tmp_content ) { $tmp = $tmp_content->URL(); if( $tmp != '' && $count == 0 ) { // try to build the url out of the parent url. $parent_url = $tmp; break; } array_unshift($stack,$tmp_content->Alias()); } $node = $node->GetParent(); $count++; } if( $parent_url != '' ) { // woot, we got a prent url. $this->mURL = $parent_url.'/'.$this->mAlias; } else { $this->mURL = implode('/',$stack); } } } } if( $this->mURL == '' && get_site_preference('content_mandatory_urls') && !$this->mDefaultContent && $this->HasUsableLink() ) { // page url is empty and mandatory $errors[] = lang('content_mandatory_urls'); } else if( $this->mURL != '' ) { // page url is not empty, check for validity. $this->mURL = strtolower(trim($this->mURL," /\t\r\n\0\x08")); // silently delete bad chars. and convert to lowercase. if( $this->mURL != '' && !content_assistant::is_valid_url($this->mURL,$this->mId) ) { // and validate the URL. $errors[] = lang('invalid_url2'); } } return (count($errors) > 0?$errors:FALSE); } /** * Delete the current content object from the database. * * @todo this function should return something, or throw an exception */ function Delete() { $gCms = cmsms(); global $debug_errors; $config = $gCms->GetConfig(); Events::SendEvent('Core', 'ContentDeletePre', array('content' => &$this)); $db = $gCms->GetDb(); $result = false; if (-1 > $this->mId) { if (true == $config["debug"]) { # :TODO: Translate the error message $debug_errors .= "

Could not delete content : invalid Id

\n"; } } else { $query = "DELETE FROM ".cms_db_prefix()."content WHERE content_id = ?"; $dbresult = $db->Execute($query, array($this->mId)); if (! $dbresult) { if (true == $config["debug"]) { # :TODO: Translate the error message $debug_errors .= "

Error deleting content

\n"; } } // Fix the item_order if necessary $query = "UPDATE ".cms_db_prefix()."content SET item_order = item_order - 1 WHERE parent_id = ? AND item_order > ?"; $result = $db->Execute($query,array($this->ParentId(),$this->ItemOrder())); $cachefilename = TMP_CACHE_LOCATION . '/contentcache.php'; @unlink($cachefilename); // DELETE properties $query = 'DELETE FROM '.cms_db_prefix().'content_props WHERE content_id = ?'; $result = $db->Execute($query,array($this->mId)); $this->_props = null; // Delete additional editors. $query = 'DELETE FROM '.cms_db_prefix().'additional_users WHERE content_id = ?'; $result = $db->Execute($query,array($this->mId)); $this->mAdditionalEditors = null; // Delete route if( $this->mURL != '' ) { cms_route_manager::del_static($this->mURL); } } Events::SendEvent('Core', 'ContentDeletePost', array('content' => &$this)); } /** * Function for the subclass to parse out data for it's parameters (usually from $_POST) * This method is typically called from an editor form to allow modifying the content object from * form input fields. * * @abstract * @return void */ public function FillParams($params,$editing = false) { // content property parameters $parameters = array('extra1','extra2','extra3','image','thumbnail'); foreach ($parameters as $oneparam) { if (isset($params[$oneparam])) { $this->SetPropertyValue($oneparam, $params[$oneparam]); } } // go through the list of base parameters // setting them from params // title if (isset($params['title'])) { $this->mName = $params['title']; } // menu text if (isset($params['menutext'])) { $this->mMenuText = $params['menutext']; } // parent id if( isset($params['parent_id']) ) { if ($this->mParentId != $params['parent_id']) { $this->mHierarchy = ''; $this->mItemOrder = -1; } $this->mParentId = $params['parent_id']; } // active if (isset($params['active'])) { $this->mActive = $params['active']; if( $this->DefaultContent() ) { $this->mActive = 1; } } // show in menu if (isset($params['showinmenu'])) { $this->mShowInMenu = $params['showinmenu']; } // alias $tmp = ''; if( isset($params['alias']) ) { $tmp = trim($params['alias']); } if( !$editing || isset($params['alias']) ) { // the alias param may not exist (depending upon permissions) // this method will set the alias to the supplied value if it is set // or auto-generate one, when adding a new page. $this->SetAlias($tmp); } // target if (isset($params['target'])) { $val = $params['target']; if( $val == '---' ) { $val = ''; } $this->SetPropertyValue('target', $val); } // title attribute if (isset($params['titleattribute'])) { $this->mTitleAttribute = $params['titleattribute']; } // accesskey if (isset($params['accesskey'])) { $this->mAccessKey = $params['accesskey']; } // tab index if (isset($params['tabindex'])) { $this->mTabIndex = $params['tabindex']; } // cachable if (isset($params['cachable'])) { $this->mCachable = $params['cachable']; } else { $this->_handleRemovedBaseProperty('cachable','mCachable'); } // secure if (isset($params['secure'])) { $this->mSecure = $params['secure']; } else { $this->_handleRemovedBaseProperty('secure','mSecure'); } // url if (isset($params['page_url'])) { $this->mURL = $params['page_url']; } else { $this->_handleRemovedBaseProperty('page_url','mURL'); } // owner if (isset($params["ownerid"])) { $this->SetOwner($params["ownerid"]); } // additional editors if (isset($params["additional_editors"])) { $addtarray = array(); if( is_array($params['additional_editors']) ) { foreach ($params["additional_editors"] as $addt_user_id) { $addtarray[] = $addt_user_id; } } $this->SetAdditionalEditors($addtarray); } } /** * A function to get the internally generated URL for this content type. * This method may be overridden by content types. * * @param boolean if true, and mod_rewrite is enabled, build a URL suitable for mod_rewrite. * @return string */ public function GetURL($rewrite = true) { $gCms = cmsms(); $config = $gCms->GetConfig(); $url = ""; $alias = ($this->mAlias != ''?$this->mAlias:$this->mId); $base_url = $config['root_url']; if( $this->Secure() ) { if( isset($config['ssl_url']) ) { $base_url = $config['ssl_url']; } else { $base_url = str_replace('http://','https://',$base_url); } } /* use root_url for default content */ if($this->mDefaultContent) { $url = $base_url . '/'; return $url; } if ($config["url_rewriting"] == 'mod_rewrite' && $rewrite == true) { $str = $this->HierarchyPath(); if( $this->mURL != '') { // we have a url path $str = $this->mURL; } $url = $base_url. '/' . $str . (isset($config['page_extension'])?$config['page_extension']:'.html'); } else if (isset($_SERVER['PHP_SELF']) && $config['url_rewriting'] == 'internal' && $rewrite == true) { $str = $this->HierarchyPath(); if( $this->mURL != '') { // we have a url path $str = $this->mURL; } $url = $base_url . '/index.php/' . $str . (isset($config['page_extension'])?$config['page_extension']:'.html'); } else { $url = $base_url . '/index.php?' . $config['query_var'] . '=' . $alias; } return $url; } /** * Show the content * * @abstract * @param string An optional property name to display. */ public function Show($param = '') { } /** * Used from a page that allows content editing. This method the list of distinct sections * that devides up the various logical sections that this content type supports for editing. * * @abstract * @return array List of tab name strings. */ public function TabNames() { return array(); } /** * Used from within a page that allows editing content this method returns the input fields for * the various fields that can be edited. * * @abstract * @param boolean Wether we are adding a new content page, or editing an existing content page. * @param int The tab index. * @param boolean unused?? */ public function EditAsArray($adding = false, $tab = 0, $showadmin = false) { # :TODO: return array(array('Error','Edit Not Defined!')); } /** * Show Help # disabled to see if its used. public function Help() { # :TODO: return "Help Not Defined"; } */ /** * Method to indicate wether the current page has children. * * @param boolean should we test only for active children. * @return booelan */ public function HasChildren($activeonly = false) { $hm = cmsms()->GetHierarchyManager(); $node = $hm->getNodeById($this->mId); if( !$node->has_children() ) return false; if( $activeonly == false) return true; $children = $node->get_children(); if( $children ) { for( $i = 0; $i < count($children); $i++ ) { $content = $children[$i]->getContent(); if( $content->Active() ) return true; } } return false; } /** * Return a list of additional editors * Note: in the returned array, group id's are specified as negative integers. * * @return mixed Array of uids and group ids, or null */ public function GetAdditionalEditors() { if (!isset($this->mAdditionalEditors)) { $gCms = cmsms(); $db = $gCms->GetDb(); $this->mAdditionalEditors = array(); $query = "SELECT user_id FROM ".cms_db_prefix()."additional_users WHERE content_id = ?"; $dbresult = $db->Execute($query,array($this->mId)); while ($dbresult && !$dbresult->EOF) { $this->mAdditionalEditors[] = $dbresult->fields['user_id']; $dbresult->MoveNext(); } if ($dbresult) $dbresult->Close(); } return $this->mAdditionalEditors; } /** * Set the list of additional editors * Note: in the provided array, group id's are specified as negative integers. * * @param mixed Array of uids and group ids, or null */ public function SetAdditionalEditors($editorarray) { $this->mAdditionalEditors = $editorarray; } static public function GetAdditionalEditorInput($addteditors,$owner_id = -1) { $ret[] = lang('additionaleditors'); $text = ''; $text .= ''; $ret[] = $text; return $ret; } /** * Provides an input element to display the list of additional editors. * This method is usually called from within this object. * * @param mixed An optional array of additional editor id's (group ids specified with negative values) * @return string The input element. */ public function ShowAdditionalEditors($addteditors = '') { $ret = array(); if( $addteditors == '' ) { $addteditors = $this->GetAdditionalEditors(); } return self::GetAdditionalEditorInput($addteditors,$this->Owner()); } /** * Indicates wether this content type can be the default page for a CMSMS website * * @abstract * @returns boolean */ public function IsDefaultPossible() { return FALSE; } /** * Set a flag indicating that we are adding a new content object and not editing an existing one * This is usually called from a script that provides the ability to add content objects. */ public function SetAddMode($flag = true) { $this->_add_mode = $flag; } /* private */ private function _handleRemovedBaseProperty($name,$member) { if( !is_array($this->_attributes) ) return FALSE; if( !in_array($name,$this->_attributes) ) { if( isset($this->_prop_defaults[$name]) ) { $this->$member = $this->_prop_defaults[$name]; return TRUE; } } return FALSE; } /** * @deprecated */ public function AddExtraProperty($name,$type = 'string') { return; // debug } /** * Remove a property from the known property list. * Specify a default value to use if the property is called. * * @param string The property name * @param string The default value. */ protected function RemoveProperty($name,$dflt) { if( !is_array($this->_attributes) ) return; $tmp = array(); for( $i = 0; $i < count($this->_attributes); $i++ ) { if( is_array($this->_attributes[$i]) && $this->_attributes[$i][0] == $name ) { continue; } $tmp[] = $this->_attributes[$i]; } $this->_attributes = $tmp; $this->_prop_defaults[$name] = $dflt; } /* * Add a property that is directly associtated with a field in the content table. * * @param string The property name * @param integer The priority * @param boolean Whether this field is required for this content type * @param string (optional) unused. */ protected function AddBaseProperty($name,$priority,$is_required = 0,$type = 'string') { if( !is_array($this->_attributes) ) { $this->_attributes = array(); } $this->_attributes[] = array($name,$priority,$is_required); } /* * Alias for AddBaseProperty */ protected function AddContentProperty($name,$priority,$is_required = 0,$type = 'string') { return $this->AddBaseProperty($name,$priority,$is_required,$type); } /* * Given a string, see if it's a known property name. * * @param string The property name * @return boolean */ protected function is_known_property($str) { $tmp = array(); foreach( $this->_attributes as $one ) { $tmp[] = $one[0]; } return in_array($str,$tmp); } /* * Given the list of registered properties, cross reference with our * basic attributes list, and figure out which ones should be displayed * * @return array of input elements of attributes that can be edited for this form. */ protected function display_attributes($adding,$negative = 0) { // get our required attributes $basic_attributes = array(); foreach( $this->_attributes as $one ) { if( $one[2] == 1 ) $basic_attributes[] = $one; } // merge in preferred basic attributes $tmp = get_site_preference('basic_attributes'); if( !empty($tmp) ) { $tmp = explode(',',$tmp); foreach( $tmp as $basic ) { $found = NULL; foreach( $this->_attributes as $one ) { if( $one[0] == $basic ) { $found = $one; break; } } if( $found ) { $basic_attributes[] = $found; } } } $attrs = $basic_attributes; if( $negative ) { // build a new list of all properties... except those in the basic_attributes $attrs = array(); foreach( $this->_attributes as $one ) { $found = 0; foreach( $basic_attributes as $basic ) { if( $basic[0] == $one[0] ) { $found = 1; } } if( !$found ) { $attrs[] = $one; } } } // remove any duplicates $tmp = array(); foreach( $attrs as $one ) { $found = 0; foreach( $tmp as $t1 ) { if( $one[0] == $t1[0] ) { $found = 1; break; } } if( !$found ) { $tmp[] = $one; } } $attrs = $tmp; // sort the attributes on the 2nd element... $fn = create_function('$a,$b','if( $a[1] < $b[1] ) return -1; else if( $a[1] == $b[1] ) return 0; else return $b;'); usort($attrs,$fn); $tmp = $this->display_admin_attributes($attrs,$adding); return $tmp; } /* private */ private function display_admin_attributes($attributelist,$adding) { // sort the attributes $ret = array(); foreach( $attributelist as $one ) { $tmp = $this->display_single_element($one[0],$adding); if( is_array($tmp) ) { $ret[] = $tmp; } } return $ret; } /** * A method to display a single input element for an object basic, or extended property. * * @abstract * @param string The property name * @param boolean Wether or not we are in add or edit mode. * @return array consisting of two elements. A label, and the input element. */ protected function display_single_element($one,$adding) { $gCms = cmsms(); $config = $gCms->GetConfig(); switch( $one ) { case 'cachable': return array('', 'mCachable?' checked="checked"':'').' />',lang('help_page_cachable')); break; case 'title': { return array(':',''); } break; case 'menutext': { return array(':',''); } break; case 'parent': { $contentops = $gCms->GetContentOperations(); $tmp = $contentops->CreateHierarchyDropdown($this->mId, $this->mParentId, 'parent_id', 0, 1, 0, 1,get_site_preference('listcontent_showtitle',true) ); if( empty($tmp) && !check_permission(get_userid(),'Manage All Content') ) return array('',''); if( !empty($tmp) ) return array(':',$tmp); } break; case 'active': if( !$this->DefaultContent() ) { return array(':','mActive?' checked="checked"':'').' />'); } break; case 'showinmenu': return array(':','mShowInMenu?' checked="checked"':'').' />'); break; case 'target': { $text = ''; $text .= ''; $text .= ''; $text .= ''; $text .= ''; return array(':','', lang('info_target')); } break; case 'alias': return array(':','',lang('help_page_alias')); break; case 'secure': { $opt = ''; if( $this->mSecure ) { $opt = ' checked="checked"'; } $str = ''; $str .= ''; return array(':',$str); } break; case 'page_url': if( !$this->DefaultContent() ) { $str = ''; $prompt = ':'; if( get_site_preference('content_mandatory_urls',0) ) { $prompt = '*'.$prompt; } return array($prompt,$str,lang('help_page_url')); } break; case 'image': { $dir = cms_join_path($config['image_uploads_path'],get_site_preference('content_imagefield_path')); $data = $this->GetPropertyValue('image'); $dropdown = create_file_dropdown('image',$dir,$data,'jpg,jpeg,png,gif','',true,'','thumb_',1,1); if( !$dropdown ) return; return array(':',$dropdown); } break; case 'thumbnail': { $dir = cms_join_path($config['image_uploads_path'],get_site_preference('content_thumbnailfield_path')); $data = $this->GetPropertyValue('thumbnail'); $dropdown = create_file_dropdown('thumbnail',$dir,$data,'jpg,jpeg,png,gif','',true,'','thumb_',0,1); if( !$dropdown ) return FALSE; return array(':',$dropdown); } break; case 'titleattribute': { return array(':',''); } break; case 'accesskey': { return array(':',''); } break; case 'tabindex': { return array(':',''); } break; case 'extra1': { return array(':',''); } break; case 'extra2': { return array(':',''); } break; case 'extra3': { return array(':',''); } break; case 'owner': { $showadmin = check_ownership(get_userid(), $this->Id()); $userops = $gCms->GetUserOperations(); if (!$adding && ($showadmin || check_permission(get_userid(),'Manage All Content')) ) { return array(':', $userops->GenerateDropdown($this->Owner())); } } break; case 'additionaleditors': { // do owner/additional-editor stuff if( $adding || check_ownership(get_userid(),$this->Id()) || check_permission(get_userid(),'Manage All Content')) { return $this->ShowAdditionalEditors(); } } break; default: stack_trace(); die('unknown property '.$one); } } protected function SetError($str) { // we don't need this being serialized. cms_utils::set_app_data('content_error',$str); } public function GetError() { $str = cms_utils::get_app_data('content_error'); return $str; } } ?>