pepiscms/modules/pages/controllers/PagesAdmin.php
<?php
/**
* PepisCMS
*
* Simple content management system
*
* @package PepisCMS
* @author Piotr Polak
* @copyright Copyright (c) 2007-2018, Piotr Polak
* @license See LICENSE.txt
* @link http://www.polak.ro/
*/
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Pages admin module
*/
class PagesAdmin extends ModuleAdminController
{
/**
* Base path for file uploads
*
* @var String
*/
private $uploads_base_path = './application/cache/tmp/'; // Overwritten by constructor
public function __construct()
{
parent::__construct();
// Overwriting uploads base path
$this->uploads_base_path = $this->config->item('uploads_path') . 'pages/';
$this->load->library(array('SimpleSessionMessage', 'Cachedobjectmanager', 'FormBuilder'));
$this->load->model(array('Menu_model', 'Page_model', 'Site_language_model', 'User_model'));
$this->load->helper(array('date', 'string'));
$this->load->moduleLanguage('pages');
if (!$this->db->table_exists($this->Page_model->getTable())) {
show_error($this->lang->line('pages_pages_database_not_configured'));
}
if (count($this->Site_language_model->getLanguageCodes()) == 0) {
$this->Site_language_model->saveById(false, array(
'code' => 'en', 'label' => 'English', 'is_default' => true, 'ci_language' => 'english'
));
}
$this->assign('site_language', $this->Site_language_model->getLanguageByCode($this->input->getParam('language_code')))
->assign('view', $this->_getView());
}
public function index()
{
$this->benchmark->mark('pages_gather_data_start');
$site_language = $this->getAttribute('site_language');
$pages = $menu = array();
if ($site_language) {
$menu = $this->Menu_model->getMenu(0, $site_language->code);
$pages = $this->Page_model->getNoMenuPages($site_language->code);
}
$this->assign('simple_session_message', $this->simplesessionmessage->getLocalizedMessage())
->assign('url_suffix', $this->config->item('url_suffix'))
->assign('menu', $menu)
->assign('pages', $pages)
->assign('site_languages', $this->Site_language_model->getLanguages());
$this->benchmark->mark('pages_gather_data_end');
$this->display($this->getAttribute('view') == 'tree' ? false : 'index_simple');
}
public function setviewtype()
{
$view = $this->input->getParam('view');
$this->auth->setSessionVariable('pages_view', $view);
$this->redirectBack($view);
}
public function edit()
{
$url_suffix = $this->config->item('url_suffix');
$site_language = $this->getAttribute('site_language');
$view = $this->input->getParam('view');
$page_id = $this->input->getParam('page_id');
$input_groups = array(
'document' => $this->lang->line('pages_label_document_contents'),
'menu' => $this->lang->line('pages_input_group_menu'),
'seo' => $this->lang->line('pages_input_group_seo'),
'info' => $this->lang->line('pages_input_info')
);
$this->formbuilder->setId($page_id);
list($definition, $menu_item) = $this->_getPageEditDefinition($input_groups, $page_id, $site_language);
$this->formbuilder->setTable('pages', false, 'page_id')
->setBackLink(module_url() . 'index/language_code-' . $site_language->code . ($view ? '/view-' . $view : ''))
->setCallback(array($this, '_on_page_save_callback'), FormBuilder::CALLBACK_ON_SAVE)
->setCallback(array($this, '_on_page_read_callback'), FormBuilder::CALLBACK_ON_READ)
->setDefinition($definition)
->setTitle($input_groups['document'])
->setRenderer(new FloatingFormRenderer())
->getRenderer()->setErrorDelimiters(get_warning_begin(), get_warning_end());
if ($this->formbuilder->getId()) {
$this->formbuilder->setApplyButtonEnabled();
}
$this->assign('menu_item', $menu_item)
->assign('form', $this->formbuilder->generate())
->assign('url_suffix', $url_suffix)
->assign('view', $view)
->display();
}
public function _on_page_read_callback(&$object)
{
$page = $this->Page_model->getById($this->formbuilder->getId());
$menuitem = $this->getAttribute('menu_item');
$object = (object)array_merge((array)$page, (array)$menuitem);
$this->assign('page', $page);
}
public function _on_page_save_callback(&$data)
{
/*
* TESTS
* Update page contents
* Write new page
* Attach new page to menu
* Attach existing page to menu
* Unpin page to menu
* OK Try to attach page and duplicate menu item name
* Try to unpin element that has children
* Try to duplicate URL
*/
$site_language = $this->getAttribute('site_language');
$current_menu_item = $this->formbuilder->getId() ? $this->Menu_model->getElementByPageId($this->formbuilder->getId()) : false;
$was_page_attached_to_menu = ($current_menu_item) ? true : false;
$is_page_attached_to_menu = false;
$is_new_page = !$this->formbuilder->getId();
if (strlen($data['page_uri']) == 0) {
$data['page_uri'] = niceuri($data['page_title']);
}
if (strlen($data['page_uri']) == 0) {
$this->formbuilder->setValidationErrorMessage($this->lang->line('pages_dialog_page_uri_cannot_be_empty'));
return false;
}
if ($is_new_page) {
// New pages
if ($this->Page_model->isUriTaken($data['page_uri'], $site_language->code)) {
$this->formbuilder->setValidationErrorMessage($this->lang->line('pages_dialog_uri_already_exists'));
return false;
}
} else {
// Existing pages
$page = $this->Page_model->getById($this->formbuilder->getId(), 'page_uri');
if ($page->page_uri != $data['page_uri'] && $this->Page_model->isUriTaken($data['page_uri'], $site_language->code)) {
$this->formbuilder->setValidationErrorMessage($this->lang->line('pages_dialog_uri_already_exists'));
return false;
}
}
/* Replacing \r and \n */
$data['page_description'] = str_replace(array("\n", "\n"), " ", $data['page_description']);
/*
* Is the page attached to a menu element?
* -1 indicates that no
*/
if ($data['parent_item_id'] != -1) {
$is_page_attached_to_menu = true;
// For for pages that were not attached to menu
// and for pages that were attached to another parent
if (!$was_page_attached_to_menu || $data['parent_item_id'] != $current_menu_item->parent_item_id || $data['item_name'] != $current_menu_item->item_name) {
// Pages attached to menu element first
if ($this->Menu_model->itemExists($data['item_name'], $data['parent_item_id'], $site_language->code)) {
$this->formbuilder->setValidationErrorMessage(sprintf($this->lang->line('pages_dialog_item_already_in_selected_menu_branch'), $data['item_name']));
return false;
}
}
}
// For OLD pages that were previously attached to menu and now they are not
if (!$is_new_page && !$is_page_attached_to_menu && $was_page_attached_to_menu) {
if ($this->Menu_model->hasChildren($current_menu_item->item_id)) {
$this->formbuilder->setValidationErrorMessage(sprintf($this->lang->line('has_children'), $data['item_name']));
return false;
} else {
// Unpining
$this->Menu_model->deleteById($current_menu_item->item_id);
}
}
$data['user_id_modified'] = $this->auth->getUserId();
$data['timestamp_modified'] = utc_timestamp();
if ($is_new_page) {
$data['user_id_created'] = $data['user_id_modified'];
$data['timestamp_created'] = $data['timestamp_modified'];
}
$data['language_code'] = $site_language->code;
if ($is_new_page) {
LOGGER::info('Writing a new page: ' . $data['page_title'], 'PAGES', $this->formbuilder->getId());
} else {
LOGGER::info('Updating page: ' . $data['page_title'], 'PAGES', $this->formbuilder->getId());
}
$this->Page_model->saveById($this->formbuilder->getId(), $data);
// If it is a new page, then we need to get DB ID
$page_id = $this->formbuilder->getId() ? $this->formbuilder->getId() : $this->db->insert_id();
if ($is_page_attached_to_menu) {
if ($was_page_attached_to_menu) {
$item_id = $current_menu_item->item_id;
$this->Menu_model->saveById($item_id, $data); // Updating
} else {
$data['page_id'] = $page_id;
$this->Menu_model->saveById(false, $data); // Inserting
$item_id = $this->db->insert_id();
}
}
$this->_clear_cache();
$this->formbuilder->setUseSimpleSessionMessageOnSuccess(false);
$view = $this->getAttribute('view');
$url_suffix = $this->getAttribute('url_suffix');
if ($is_new_page) {
$this->simplesessionmessage->setFormattingFunction(SimpleSessionMessage::FUNCTION_SUCCESS)
->setMessage('pages_dialog_write_new_success', '<a href="' . module_url() . 'edit/page_id-' . $page_id . '/language_code-' . $site_language->code . '">', '</a>', '<a href="' . ($site_language->is_default == 1 ? '' : $site_language->code . '/') . $data['page_uri'] . $url_suffix . '">', '</a>');
} else {
$this->simplesessionmessage->setFormattingFunction(SimpleSessionMessage::FUNCTION_SUCCESS)
->setMessage('pages_dialog_page_updated', '<a href="' . ($site_language->is_default == 1 ? '' : $site_language->code . '/') . $data['page_uri'] . $url_suffix . '">', '</a>', '<a href="' . module_url() . 'edit/page_id-' . $page_id . '/language_code-' . $site_language->code . '">', '</a>');
}
return true;
}
public function delete()
{
$page_id = $this->input->getParam('page_id');
LOGGER::info('Deleting page', 'PAGES', $page_id);
$this->_onDelete($page_id);
$success = $this->Page_model->deleteById($page_id);
$this->_clear_cache();
// Setting the message and redirecting
$this->simplesessionmessage->setFormattingFunction(SimpleSessionMessage::FUNCTION_SUCCESS)
->setMessage('pages_dialog_delete_page_success');
if ($this->expectsJsonResponse()) {
if ($success) {
$this->jsonResponse(true, 'OK');
} else {
$this->jsonResponse(false, 'Unable to page element, it might contain a submenu');
}
}
$this->redirectBack();
}
public function deletemenuelement()
{
$item_id = $this->input->getParam('item_id');
$success = false;
if (!$this->Menu_model->hasChildren($item_id)) {
$page_id = $this->Menu_model->getPageIdByItemId($item_id); // Must before the deletion code
$this->Menu_model->deleteById($item_id);
if ($page_id) {
$this->_onDelete($page_id);
$success = $this->Page_model->deleteById($page_id);
LOGGER::info('Deleting page', 'PAGES', $page_id);
}
$this->_clear_cache();
if ($this->expectsJsonResponse()) {
if ($success) {
$this->jsonResponse(true, 'OK');
} else {
$this->jsonResponse(false, 'Unable to delete menu element, it might contain a submenu');
}
}
} else {
$menuelement = $this->Menu_model->getById($item_id);
if ($this->expectsJsonResponse()) {
if ($success) {
$this->jsonResponse(true, 'OK');
} else {
$this->jsonResponse(false, sprintf($this->lang->line('pages_dialog_menu_contains_submenu_error'), $menuelement->item_name));
}
}
// Setting the message and redirecting
$this->simplesessionmessage->setFormattingFunction(SimpleSessionMessage::FUNCTION_ERROR)
->setMessage('pages_dialog_menu_contains_submenu_error', $menuelement->item_name);
}
$this->redirectBack();
}
public function move()
{
$direction = $this->input->getParam('direction');
$id = $this->input->getParam('item_id');
$this->Generic_model->move($id, $direction, $this->Menu_model->getTable(), 'parent_item_id', 'item_order', 'item_id');
$this->_clear_cache();
if ($this->expectsJsonResponse()) {
$this->jsonResponse(true, 'OK');
}
$this->redirectBack();
}
public function setdefault()
{
$site_language = $this->getAttribute('site_language');
$this->Page_model->setDefault($this->input->getParam('page_id'), $site_language->code);
$this->_clear_cache();
$this->redirectBack();
}
private function _clear_cache()
{
try {
$this->Page_model->cleanPagesCache();
} catch (Exception $e) {
}
$this->cachedobjectmanager->cleanup('pages');
}
public function menuedit()
{
$site_language = $this->getAttribute('site_language');
$item_id = $this->input->getParam('item_id');
$menu = array('0' => $this->lang->line('pages_dialog_main_menu'));
$this->formbuilder->setId($item_id)
->setTitle($this->lang->line('pages_menuedit_header'))
->setBackLink(module_url() . 'index/language_code-' . $site_language->code . '/view-' . $this->getAttribute('view'))
->setFeedObject($this->Menu_model)
->setDefinition(
array(
'parent_item_id' => array(
'input_type' => FormBuilder::SELECTBOX,
'label' => $this->lang->line('pages_label_location_in_menu'),
'values' => $this->Menu_model->getMenuFlat(0, $site_language->code, $item_id, false, $menu),
),
'item_name' => array(
'label' => $this->lang->line('pages_label_menu_item_name'),
'validation_rules' => 'trim|required|min_length[1]|callback__menu_item_name_check',
),
'item_url' => array(
'label' => $this->lang->line('pages_label_element_uri'),
'validation_rules' => 'trim|required',
),
'language_code' => array(
'input_type' => FormBuilder::HIDDEN,
'input_default_value' => $site_language->code
)
)
);
$this->assign('item_id', $item_id)
->assign('form', $this->formbuilder->generate())
->display();
}
public function flush_html_cache()
{
$this->load->helper('number');
$this->load->language('utilities');
$this->auth->refreshSession();
try {
$stats = $this->Page_model->cleanPagesCache();
$this->simplesessionmessage->setFormattingFunction(SimpleSessionMessage::FUNCTION_SUCCESS)
->setMessage('utilities_cache_successfully_cleaned', $stats['count'], byte_format($stats['size']));
} catch (Exception $e) {
$this->simplesessionmessage->setFormattingFunction(SimpleSessionMessage::FUNCTION_ERROR)
->setMessage('utilities_label_cache_unable_to_open_directory_might_be_empty');
}
// Smart redirect
$this->load->library('User_agent');
if ($this->agent->referrer()) {
redirect($this->agent->referrer());
} else {
redirect(module_url());
}
}
/**
* Callback
*
* @param $str
* @return bool
*/
public function _menu_item_name_check($str)
{
if ($this->formbuilder->getId()) {
$item = $this->Menu_model->getById($this->formbuilder->getId(), 'item_name, parent_item_id');
if ($str == $item->item_name && $_POST['parent_item_id'] == $item->parent_item_id) {
return true; // As nothing changed
}
}
if ($this->Menu_model->itemExists($str, $_POST['parent_item_id'])) {
$this->form_validation->set_message(__FUNCTION__, sprintf($this->lang->line('pages_dialog_item_already_in_selected_menu_branch'), $str));
return false;
} else {
return true;
}
}
/**
* @return mixed|string
*/
private function _getView()
{
$view = $this->input->getParam('view');
if (!$view) {
$view = $this->auth->getSessionVariable('pages_view');
if (!$view) {
$view = 'simple'; // The default
}
}
return $view;
}
/**
* @param $input_groups
* @param $page_id
* @param $site_language
* @return array
*/
private function _getPageEditDefinition($input_groups, $page_id, $site_language)
{
$definition = array();
$definition['page_title'] = array(
'input_group' => $input_groups['document'],
'validation_rules' => 'trim|required|min_length[1]',
'label' => $this->lang->line('pages_label_document_title'),
'description' => $this->lang->line('pages_label_document_title_desc'),
'input_type' => FormBuilder::TEXTFIELD,
);
$definition['page_contents'] = array(
'input_group' => $input_groups['document'],
'validation_rules' => '',
'label' => $this->lang->line('pages_label_contents'),
'description' => $this->lang->line('pages_label_contents_desc'),
'input_type' => FormBuilder::RTF,
);
$definition['page_image_path'] = array(
'input_group' => $input_groups['seo'],
'label' => $this->lang->line('pages_label_image_path'),
'description' => $this->lang->line('pages_label_image_path_desc'),
'input_type' => FormBuilder::IMAGE,
'upload_path' => $this->uploads_base_path,
'upload_display_path' => $this->uploads_base_path,
'show_in_form' => true,
'validation_rules' => 'max_length[500]',
'upload_complete_callback' => array($this, '_fb_callback_make_filename_seo_friendly'),
);
$definition['page_description'] = array(
'input_group' => $input_groups['seo'],
'validation_rules' => '',
'label' => $this->lang->line('pages_label_description'),
'description' => $this->lang->line('pages_label_description_desc'),
'input_type' => FormBuilder::TEXTAREA,
);
$definition['page_keywords'] = array(
'input_group' => $input_groups['seo'],
'validation_rules' => '',
'label' => $this->lang->line('pages_label_keywords'),
'description' => $this->lang->line('pages_label_keywords_desc'),
'input_type' => FormBuilder::TEXTFIELD,
);
$definition['page_is_displayed_in_sitemap'] = array(
'input_group' => $input_groups['seo'],
'validation_rules' => '',
'input_default_value' => 1,
'label' => $this->lang->line('pages_label_display_in_sitemap'),
'description' => $this->lang->line('pages_label_display_in_sitemap_desc'),
'input_type' => FormBuilder::CHECKBOX,
);
$definition['page_uri'] = array(
'input_group' => $input_groups['seo'],
'validation_rules' => 'trim|niceuri',
'label' => $this->lang->line('pages_label_document_uri'),
'description' => $this->lang->line('pages_label_document_uri_desc'),
);
$menu_item = $page_id ? $this->Menu_model->getElementByPageId($page_id) : false;
$menu = array('-1' => $this->lang->line('pages_dialog_hidden_menu'), '0' => $this->lang->line('pages_dialog_main_menu'));
$menu_values = $this->Menu_model->getMenuFlat(0, $site_language->code, false, false, $menu);
if ($menu_item) {
foreach ($menu_values as $key => &$dontcare) {
if ($key == $menu_item->item_id) {
unset($menu_values[$key]);
break;
}
}
}
$definition['parent_item_id'] = array(
'input_group' => $input_groups['menu'],
'label' => $this->lang->line('pages_label_location_in_menu'),
'values' => $menu_values,
'input_type' => FormBuilder::SELECTBOX,
'input_default_value' => ($this->input->getParam('parent_item_id') ? $this->input->getParam('parent_item_id') : -1) // Default -1 but if there is a get param set, use the get param
);
$definition['item_name'] = array(
'input_group' => $input_groups['menu'],
'label' => $this->lang->line('pages_label_menu_item_name'),
'input_type' => FormBuilder::TEXTFIELD,
'validation_rules' => '',
);
// Only if the page is attached to menu
if ($this->input->post('parent_item_id') != '-1') {
$definition['item_name']['validation_rules'] = 'trim|required|min_length[1]';
}
if ($this->formbuilder->getId()) {
$definition['user_id_created'] = array(
'input_group' => $input_groups['info'],
'label' => $this->lang->line('pages_label_user_id_created'),
'input_is_editable' => false,
'foreign_key_table' => $this->User_model->getTable(),
'foreign_key_field' => 'user_id',
'foreign_key_label_field' => 'user_email',
'validation_rules' => '',
);
$definition['timestamp_created'] = array(
'input_group' => $input_groups['info'],
'label' => $this->lang->line('pages_label_timestamp_created'),
'input_is_editable' => false,
'validation_rules' => '',
);
$definition['user_id_modified'] = array(
'input_group' => $input_groups['info'],
'label' => $this->lang->line('pages_label_user_id_modified'),
'input_is_editable' => false,
'foreign_key_table' => $this->User_model->getTable(),
'foreign_key_field' => 'user_id',
'foreign_key_label_field' => 'user_email',
'validation_rules' => '',
);
$definition['timestamp_modified'] = array(
'input_group' => $input_groups['info'],
'label' => $this->lang->line('pages_label_timestamp_modified'),
'input_is_editable' => false,
'validation_rules' => '',
);
}
return array($definition, $menu_item);
}
/**
* @param $status
* @param $message
*/
private function jsonResponse($status, $message)
{
$data = array();
$data['status'] = $status ? '1' : '0';
$data['message'] = $message;
$this->output->set_header('Content-Type: application/json');
die(json_encode($data));
}
/**
* @param $view
*/
private function redirectBack($view = false)
{
$site_language = $this->getAttribute('site_language');
$language_code = $site_language->code;
if (!$view) {
$view = $this->input->getParam('view');
}
redirect(module_url() . 'index/language_code-' . $language_code . ($view ? '/view-' . $view : ''));
}
/**
* Callback function changing the name of the file to SEO friendly
*
* @version: 1.2.3
* @date: 2015-06-11
*
* @param $filename
* @param $base_path
* @param $data
* @param $current_image_field_name
* @return bool
*/
public function _fb_callback_make_filename_seo_friendly(&$filename, $base_path, &$data, $current_image_field_name)
{
// List of the fields to be used, if no value is present for a given key
// then the key will be ignored. By default all values of the keys
// specified will be concatenated
$title_field_names = array('name', 'title', 'label', 'page_title');
$this->load->helper('string');
$path = $base_path . $filename;
$path_parts = pathinfo($path);
// Attempt to build a name
$new_base_filename = '';
foreach ($title_field_names as $title_field_name) {
// Concatenating all the elements
if (isset($data[$title_field_name]) && $data[$title_field_name]) {
$new_base_filename .= '-' . $data[$title_field_name];
}
}
// Making it web safe
if ($new_base_filename) {
$new_base_filename = niceuri($new_base_filename);
}
// This should not be an else statement as niceuri can return empty string sometimes
if (!$new_base_filename) {
$new_base_filename = niceuri($path_parts['filename']);
}
// This should normally never happen, but who knows - this is bulletproof
if (!$new_base_filename) {
$new_base_filename = md5(time() + rand(1000, 9999));
}
$new_base_path = '';
// $new_base_path = date('Y-m-d').'/'; // Will create directory based on date
// $new_base_path = $new_name_base.'/'; // Will create directory based on the niceuri value
// @mkdir($base_path.$new_base_path); // Do not forget!
// We don't like upper case extensions
$extension = strtolower($path_parts['extension']);
$new_name = $new_base_filename . '.' . $extension;
// Protection against existing files
$i = 2;
while (file_exists($base_path . $new_base_path . $new_name)) {
$new_name = $new_base_filename . '-' . $i . '.' . $extension;
if ($i++ > 50 || strlen($i) > 2) // strlen is a protection against the infinity loop for md5 checksums
{
// This is ridiculous but who knowss
$i = md5(time() + rand(1000, 9999));
}
}
// No need to change filename? Then we are fine
if ($filename == $new_name) {
return true;
}
// Finally here we go!
if (rename($path, $base_path . $new_base_path . $new_name)) {
$data[$current_image_field_name] = $new_base_path . $new_name;
$filename = $new_base_path . $new_name;
return true;
}
return false;
}
/**
* @param $page_id
*/
private function _onDelete($page_id)
{
$page = $this->Page_model->getById($page_id);
if (!$page) {
show_404();
}
if ($page->page_image_path) {
unlink($this->uploads_base_path . $page->page_image_path);
}
}
/**
* @return bool
*/
private function expectsJsonResponse()
{
return $this->input->getParam('json') == 1;
}
}