/** * Saves a page based on page editor info * @param mixed $input * @return mixed array * success boolean * url string -- DEPRECATED this will not always be correct due to the lack of complete route data. See the action savePage * in the front end controller for a way to generate the correct url for the updated page * pageid int -- the pageid for the update or created page */ public function pageSave($input) { $this->checkHasAdminPermission('canusesitebuilder'); /* Sample input Array ( [pageid] => 1, [screenlayoutid] => 2, [displaysections[0 => [{"widgetId":"3","widgetInstanceId":"1"},{"widgetId":"4","widgetInstanceId":"2"}], [displaysections[1 => [{"widgetId":"1","widgetInstanceId":"3"},{"widgetId":"2","widgetInstanceId":"4"}], [pagetitle] => Forums, [resturl] => forums, [pagetemplateid] => 0, // 0 if we are saving the page template as a new page template [templatetitle] => Name, [btnSaveEditPage] => ) */ $done = false; $i = 0; $displaysections = array(); foreach ($input as $key => $value) { if (!empty($value) and preg_match('/^displaysections\\[([0-9]+)$/i', $key, $matches)) { $displaysection_value = json_decode($value, true); if (!empty($displaysection_value)) { $displaysections[$matches[1]] = $displaysection_value; } } } // TODO: apparently JQuery will send POST data using the UTF-8 charset, // so we don't convert the resturl. However, if the url can be edited from anywhere // else than Site Builder, we'll need to convert it properly to ensure that the // route table gets the proper UTF-8 characters saved. // cleaning input $input = array('pagetitle' => trim(strval($input['pagetitle'])), 'resturl' => trim(strval($input['resturl']), " \t\n\r\v/"), 'pageid' => intval($input['pageid']), 'nodeid' => intval($input['nodeid']), 'userid' => intval($input['userid']), 'pagetemplateid' => intval($input['pagetemplateid']), 'templatetitle' => trim(strval($input['templatetitle'])), 'screenlayoutid' => intval($input['screenlayoutid']), 'displaysections' => $displaysections, 'metadescription' => trim(strval($input['metadescription']))); // we need to check that resturl does not contain any reserved characters. if (!$this->checkCustomUrl($input['resturl'])) { throw new vB_Exception_Api('invalid_custom_url', vB_String::INVALID_CUSTOM_URL_CHAR); } if (empty($input['pagetitle'])) { throw new vB_Exception_Api('page_title_cannot_be_empty'); } if (empty($input['templatetitle']) and $input['pagetemplateid'] < 1) { throw new vB_Exception_Api('page_template_title_cannot_be_empty'); } if ($input['screenlayoutid'] < 1) { throw new vB_Exception_Api('you_must_specify_a_screen_layout'); } $this->db = vB::getDbAssertor(); // --- save the page template ---------------------------- // get page info $forceNewPage = false; /* if prefix is modified, we need to create a new page, pagetemplate and widgets */ $isPrefixUsed = false; if ($input['pageid'] > 0) { $page = $this->fetchPageById($input['pageid'], array('nodeid' => $input['nodeid'], 'userid' => $input['userid'])); if (!is_array($page)) { $page = array(); } else { $forceNewPage = ($page['isgeneric'] and $input['resturl'] != $page['urlprefix']); // if we are modifying a page url, we need to check the new url... if ($input['resturl'] != $page['urlprefix']) { $isPrefixUsed = vB5_Route::isPrefixUsed($input['resturl'], $page['routeid']); } } } else { // if it is a new page, we need to check the url $isPrefixUsed = vB5_Route::isPrefixUsed($input['resturl']); $page = array(); } $routeApi = vB_Api::instanceInternal('route'); // if the used prefix is a 301 to another route, then we can take it. if ($isPrefixUsed !== FALSE and empty($isPrefixUsed['redirectRouteId'])) { throw new vB_Exception_Api('this_url_is_already_used'); } // page template $valuePairs = array('title' => $input['templatetitle'], 'screenlayoutid' => $input['screenlayoutid']); $pagetemplateid = $input['pagetemplateid']; if ($pagetemplateid < 1 or $forceNewPage) { $valuePairs['guid'] = vB_Xml_Export_PageTemplate::createGUID($valuePairs); // If no widgets were configured on the page template, we won't have a page template ID. $pagetemplateid = $this->db->insert('pagetemplate', $valuePairs); if (is_array($pagetemplateid)) { $pagetemplateid = (int) array_pop($pagetemplateid); } $newTemplate = true; } else { $this->db->update('pagetemplate', $valuePairs, array('pagetemplateid' => $pagetemplateid)); $newTemplate = false; } // widgets on page template $widgetApi = vB_Api::instanceInternal('widget'); $currentWidgetInstances = $widgetApi->fetchWidgetInstancesByPageTemplateId($pagetemplateid); $currentWidgetInstanceIds = $this->getAllCurrentModuleInstances($currentWidgetInstances); $savedWidgetInstanceIds = array(); $widgets = array(); foreach ($input['displaysections'] as $displaycolumn => $columnwidgets) { $displayorder = 0; foreach ($columnwidgets as $columnwidget) { $columnwidgetid = intval($columnwidget['widgetId']); $columnwidgetinstanceid = intval($columnwidget['widgetInstanceId']); if (!$columnwidgetid) { continue; } if ($newTemplate) { $widgetInstanceId = 0; } else { $widgetInstanceId = $columnwidgetinstanceid; $savedWidgetInstanceIds[$widgetInstanceId] = $columnwidgetid; } $widget = array('widgetinstanceid' => $widgetInstanceId, 'pagetemplateid' => $pagetemplateid, 'widgetid' => $columnwidgetid, 'displaysection' => $displaycolumn, 'displayorder' => $displayorder); if (isset($columnwidget['subModules'])) { $widget['subModules'] = $columnwidget['subModules']; $widget['displaySubModules'] = $columnwidget['displaySubModules']; if (!$newTemplate) { $savedWidgetInstanceIds += $this->getAllSubModulesInstances($columnwidget['subModules']); } } $widgets[] = $widget; ++$displayorder; } } // check we are not adding a system widget $newWidgets = array_diff_key($savedWidgetInstanceIds, $currentWidgetInstanceIds); if ($newWidgets) { foreach ($newWidgets as $widgetId) { if ($widgetApi->isSystemWidget($widgetId)) { throw new vB_Exception_Api('cannot_add_system_module'); } } } // check we are not removing a system widget $deleteWidgets = array_diff_key($currentWidgetInstanceIds, $savedWidgetInstanceIds); if ($deleteWidgets) { foreach ($deleteWidgets as $widgetId) { if ($widgetApi->isSystemWidget($widgetId)) { throw new vB_Exception_Api('cannot_remove_system_module'); } } } // save widget placements on the page template foreach ($widgets as $widget) { $widgetinstanceid = $widget['widgetinstanceid']; unset($widget['widgetinstanceid']); $subModules = isset($widget['subModules']) ? $widget['subModules'] : array(); unset($widget['subModules']); $displaySubModules = isset($widget['displaySubModules']) ? $widget['displaySubModules'] : array(); unset($widget['displaySubModules']); if ($widgetinstanceid > 0 and !$forceNewPage) { $this->db->update('widgetinstance', $widget, array('widgetinstanceid' => $widgetinstanceid)); } else { $widgetinstanceid = $this->db->insert('widgetinstance', $widget); if (is_array($widgetinstanceid)) { $widgetinstanceid = (int) array_pop($widgetinstanceid); } } // save submodules if available if (!empty($subModules)) { $this->saveSubModules($pagetemplateid, $widgetinstanceid, $subModules, $displaySubModules, $forceNewPage); } } // remove any widgets that have been removed from the page template if (!empty($deleteWidgets)) { $deleted = $widgetApi->deleteWidgetInstances(array_keys($deleteWidgets)); if ($deleted != count($deleteWidgets)) { throw new vB_Exception_Api('unable_to_delete_widget_instances'); } } // --- save the page --------------------------------- // permalink $urlprefix = $input['resturl']; $valuePairs = array('pagetemplateid' => $pagetemplateid); // save page if (!empty($page) and !$forceNewPage) { // update page record $conditions = array('pageid' => $page['pageid']); $this->save($valuePairs, $conditions); $pageid = $page['pageid']; $guidforphrase = vB_Library::instance('phrase')->cleanGuidForPhrase($page['guid']); // update this page's current route if needed if ($input['resturl'] != $page['urlprefix']) { $data = array('prefix' => $urlprefix); if (isset($input['nodeid']) and !empty($input['nodeid'])) { $data['nodeid'] = $input['nodeid']; } vB5_Route::updateRoute($page['routeid'], $data); } } else { $valuePairs['guid'] = vB_Xml_Export_Page::createGUID($valuePairs); $guidforphrase = vB_Library::instance('phrase')->cleanGuidForPhrase($valuePairs['guid']); // insert a new page $pageid = $this->save($valuePairs); if (is_array($pageid)) { $pageid = (int) array_pop($pageid); } // route if (isset($page['routeid'])) { // update this page's current route $data = array('pageid' => $pageid, 'prefix' => $urlprefix, 'nodeid' => $input['nodeid']); $routeid = vB5_Route::updateRoute($page['routeid'], $data); } else { $valuePairs = array('prefix' => $urlprefix, 'contentid' => $pageid); $routeid = $routeApi->createRoute('vB5_Route_Page', $valuePairs); } if (is_array($routeid)) { $routeid = (int) array_pop($routeid); } // update page with routeid (for deleting it when deleting a page) $routeApi->updateNewPageRoute($pageid, $routeid); // VBV-13666. Since $forceNewPage is true, we have created a new // page record corresponding to $pageid. But it appears that the // actual page being used is still the old page record. Thus we need // to update the routeid in the old page record so that the redirect // works correctly (the page route class gets the cannonical route // record based on the routeid in the page record). When/if we fix/change // the behavior for creating a new page record when updating the page // route prefix, this code will be wrong-- instead, we will need code // in the if branch above (for updating a page as opposed to creating // a new one), to update the routeid in the page record. if (!empty($page['pageid'])) { $routeApi->updateNewPageRoute($page['pageid'], $routeid); } } // Insert/Update phrases for page title, meta description. // Only update phrases of current language. Keep other translations. $phraseLib = vB_Library::instance('phrase'); $currentlanguageid = vB::getCurrentSession()->get('languageid'); if (empty($currentlanguageid)) { $currentlanguageid = vB::getDatastore()->getOption('languageid'); } $translations = vB::getDbAssertor()->assertQuery('phrase', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, vB_dB_Query::CONDITIONS_KEY => array(array('field' => 'varname', 'value' => array('page_' . $guidforphrase . '_title', 'page_' . $guidforphrase . '_metadesc'), 'operator' => vB_dB_Query::OPERATOR_EQ), array('field' => 'fieldname', 'value' => 'pagemeta', 'operator' => vB_dB_Query::OPERATOR_EQ), array('field' => 'languageid', 'value' => array(-1, $currentlanguageid), 'operator' => vB_dB_Query::OPERATOR_NE)))); $pagetrans = array('page_' . $guidforphrase . '_title' => array(), 'page_' . $guidforphrase . '_metadesc' => array()); $hasdefault = array(); $productid = false; foreach ($translations as $translation) { if (!$productid) { $productid = $translation['product']; } $pagetrans[$translation['varname']][$translation['languageid']] = $translation['text']; if ($translation['languageid'] == 0) { $hasdefault[$translation['varname']] = true; } } // Add input text to translates $pagetrans['page_' . $guidforphrase . '_title'][$currentlanguageid] = $input['pagetitle']; $pagetrans['page_' . $guidforphrase . '_metadesc'][$currentlanguageid] = $input['metadescription']; foreach (array_keys($pagetrans) as $varname) { if (empty($hasdefault[$varname]) or $currentlanguageid == vB::getDatastore()->getOption('languageid')) { // If the page phrase doesn't have a default one (languageid = 0) or current language is default language // We should update the phrase for default language (languageid = 0) $pagetrans[$varname][0] = $pagetrans[$varname][$currentlanguageid]; } } if (!$productid) { $page = vB::getDbAssertor()->getColumn('page', 'product', array('pageid' => $pageid)); $productid = array_pop($page); } $phraseLib->save('pagemeta', 'page_' . $guidforphrase . '_title', array('text' => $pagetrans['page_' . $guidforphrase . '_title'], 'product' => $productid, 'oldvarname' => 'page_' . $guidforphrase . '_title', 'oldfieldname' => 'global', 'skipdebug' => 1)); $phraseLib->save('pagemeta', 'page_' . $guidforphrase . '_metadesc', array('text' => $pagetrans['page_' . $guidforphrase . '_metadesc'], 'product' => $productid, 'oldvarname' => 'page_' . $guidforphrase . '_metadesc', 'oldfieldname' => 'global', 'skipdebug' => 1)); build_language(); vB_Cache::instance()->event('pageChg_' . $pageid); $page = $this->fetchPageById($pageid, array('nodeid' => $input['nodeid'], 'userid' => $input['userid'])); return array('success' => true, 'url' => $page['url'], 'pageid' => $pageid); }
/** * Creates pagetemplate, pages and routes for a channel * @param int nodeid * @param array $data - Must contain the following fields: * - templates * - vB5_Route_Channel * - vB5_Route_Conversation (optional) * - parentid * - title * - page_parentid */ protected function createChannelPages($nodeid, $data) { $db = vB::getDbAssertor(); // default to creating a forum/channel, not a category if (!isset($data['category'])) { $data['category'] = 0; } // Default child route & channel/child templates. Note, if you set a childroute, // you should also set the $data['templates'][vB5_Route_Channel'] & $data['templates'][$childRoute] appropriately $childRoute = isset($data['childroute']) ? $data['childroute'] : 'vB5_Route_Conversation'; $childTemplate = vB_Page::getConversPageTemplate(); $channelTemplate = vB_Page::getChannelPageTemplate(); if (!isset($data['templates']) || !isset($data['templates']['vB5_Route_Channel']) || !isset($data['templates'][$childRoute])) { $parentChannel = $this->getContent($data['parentid']); if (isset($parentChannel[$data['parentid']])) { $parentChannel = $parentChannel[$data['parentid']]; } if ($parentChannel['category'] != $data['category']) { // we cannot inherit the templates, use the default ones if ($data['category'] > 0) { $templates['vB5_Route_Channel'] = vB_Page::getCategoryChannelPageTemplate(); } else { $templates['vB5_Route_Channel'] = $channelTemplate; $templates[$childRoute] = $childTemplate; } } else { // Get page templates used by parent $templates = array(); $parentRoutes = $db->assertQuery('routenew', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, vB_dB_Query::CONDITIONS_KEY => array(array('field' => 'class', 'value' => array('vB5_Route_Channel', $childRoute)), array('field' => 'contentid', 'value' => $data['parentid']), array('field' => 'redirect301', 'operator' => vB_dB_Query::OPERATOR_ISNULL)))); $routeInfo = array(); foreach ($parentRoutes as $parentRoute) { $args = unserialize($parentRoute['arguments']); $routeInfo[$parentRoute['class']] = $args['pageid']; } $parentPages = $db->assertQuery('page', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, vB_dB_Query::CONDITIONS_KEY => array(array('field' => 'pageid', 'value' => array_values($routeInfo))))); foreach ($parentPages as $parentPage) { foreach ($routeInfo as $class => $pageId) { if ($pageId == $parentPage['pageid']) { // don't use template from forum homepage if ($class == 'vB5_Route_Channel' and $parentPage['pagetemplateid'] == 1) { $masterTemplate = vB::getDbAssertor()->getRow('pagetemplate', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'guid' => vB_Page::TEMPLATE_CHANNEL)); $templates[$class] = $masterTemplate['pagetemplateid']; } else { $templates[$class] = $parentPage['pagetemplateid']; } $parentPageIds[$class] = $pageId; } } } } } else { $templates = $data['templates']; unset($data['templates']); } // check if the main channel page already exists $existingRouteId = (int) vB::getDbAssertor()->getField('vBForum:node', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, vB_dB_Query::COLUMNS_KEY => array('routeid'), vB_dB_Query::CONDITIONS_KEY => array(array('field' => 'nodeid', 'value' => $nodeid, 'operator' => vB_dB_Query::OPERATOR_EQ)))); if ($existingRouteId > 0) { $existingPage = vB::getDbAssertor()->getRow('page', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, vB_dB_Query::CONDITIONS_KEY => array(array('field' => 'routeid', 'value' => $existingRouteId, 'operator' => vB_dB_Query::OPERATOR_EQ)))); } else { $existingPage = array(); } $phraseLib = vB_Library::instance('phrase'); if (empty($existingPage)) { // Create main channel page $page['guid'] = vB_Xml_Export_Page::createGUID(array()); $page['pagetemplateid'] = $templates['vB5_Route_Channel']; $page['title'] = $data['title']; $page['pagetype'] = vB_Page::TYPE_CUSTOM; $page['parentid'] = isset($data['page_parentid']) ? $data['page_parentid'] : (isset($parentPageIds['vB5_Route_Channel']) ? $parentPageIds['vB5_Route_Channel'] : 0); $pageid = $db->insert('page', $page); if (is_array($pageid)) { $pageid = (int) array_pop($pageid); } $guidforphrase = vB_Library::instance('phrase')->cleanGuidForPhrase($page['guid']); $newpage = vB::getDbAssertor()->getColumn('page', 'product', array('pageid' => $pageid)); $productid = array_pop($newpage); $phraseLib->save('pagemeta', 'page_' . $guidforphrase . '_title', array('text' => array($page['title']), 'product' => $productid, 'oldvarname' => 'page_' . $guidforphrase . '_title', 'oldfieldname' => 'global', 'skipdebug' => 1)); if (isset($data['description'])) { $phraseLib->save('pagemeta', 'page_' . $guidforphrase . '_metadesc', array('text' => array($data['description']), 'product' => $productid, 'oldvarname' => 'page_' . $guidforphrase . '_metadesc', 'oldfieldname' => 'global', 'skipdebug' => 1)); } // Create route for main channel page $route_data = array('nodeid' => $nodeid, 'pageid' => $pageid); if (!empty($data['routeguid'])) { $route_data['guid'] = $data['routeguid']; } if (!empty($data['urlident'])) { $route_data['urlident'] = $data['urlident']; } $channelRouteId = vB_Api::instanceInternal('route')->createRoute('vB5_Route_Channel', $route_data); if (is_array($channelRouteId)) { $channelRouteId = (int) array_pop($channelRouteId); } $db->update('vBForum:node', array('routeid' => $channelRouteId), array('nodeid' => $nodeid)); $db->update('page', array('routeid' => $channelRouteId), array('pageid' => $pageid)); } else { //Update the existing main channel page $page['pagetemplateid'] = $existingPage['pagetemplateid']; $page['title'] = $data['title']; $page['pagetype'] = $existingPage['pagetype']; $page['parentid'] = isset($data['page_parentid']) ? $data['page_parentid'] : (isset($parentPageIds['vB5_Route_Channel']) ? $parentPageIds['vB5_Route_Channel'] : 0); $pageid = $existingPage['pageid']; $db->update('page', $page, array('pageid' => $pageid)); $productid = $existingPage['product']; } vB_Cache::instance(vB_Cache::CACHE_FAST)->event("nodeChg_{$nodeid}"); vB_Cache::instance()->event("nodeChg_{$nodeid}"); if ($data['category'] == 0 and isset($templates[$childRoute]) and !empty($templates[$childRoute])) { // Create the conversation page $page['guid'] = vB_Xml_Export_Page::createGUID(array()); $page['pagetemplateid'] = $templates[$childRoute]; $page['title'] = $data['title']; $page['pagetype'] = vB_Page::TYPE_DEFAULT; $page['parentid'] = $pageid; $pageid = $db->insert('page', $page); if (is_array($pageid)) { $pageid = (int) array_pop($pageid); } $guidforphrase = vB_Library::instance('phrase')->cleanGuidForPhrase($page['guid']); $phraseLib->save('pagemeta', 'page_' . $guidforphrase . '_title', array('text' => array($page['title']), 'product' => $productid, 'oldvarname' => 'page_' . $guidforphrase . '_title', 'oldfieldname' => 'global', 'skipdebug' => 1)); if (isset($data['description'])) { $phraseLib->save('pagemeta', 'page_' . $guidforphrase . '_metadesc', array('text' => array($data['description']), 'product' => $productid, 'oldvarname' => 'page_' . $guidforphrase . '_metadesc', 'oldfieldname' => 'global', 'skipdebug' => 1)); } // Create route for conversation page $conversationRouteId = vB_Api::instanceInternal('route')->createRoute($childRoute, array('channelid' => $nodeid, 'pageid' => $pageid)); if (is_array($conversationRouteId)) { $conversationRouteId = (int) array_pop($conversationRouteId); } $db->update('page', array('routeid' => $conversationRouteId), array('pageid' => $pageid)); } }