<?php
/**
* @package VikBooking
* @subpackage com_vikbooking
* @author E4J srl
* @copyright Copyright (C) e4j - Extensionsforjoomla.com. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
* @link https://vikwp.com
*/
// No direct access
defined('ABSPATH') or die('No script kiddies please!');
JLoader::import('adapter.mvc.models.form');
/**
* VikBooking plugin Shortcode model.
*
* @since 1.0
* @see JModelForm
*/
class VikBookingModelShortcode extends JModelForm
{
/**
* @override
* This method should be used to pre-load an item considering
* the data set in the request.
*
* For example, if the request owns an ID, this method may try
* to retrieve the item from the database.
* Otherwise it may return an empty object.
*
* @return object The object found.
*/
public function loadFormData()
{
$input = JFactory::getApplication()->input;
$id = $input->getUint('cid', array(0));
return $this->getItem(array_shift($id));
}
/**
* This method should be used to retrieve the posted data
* after the form submission.
*
* @return object The data object.
*/
public function getFormData()
{
$input = JFactory::getApplication()->input;
// get data from request
$data = new stdClass;
$data->id = $input->getInt('id', 0);
$data->title = '';
$data->name = $input->getString('name');
$data->type = $input->getString('type');
$data->lang = $input->getString('lang');
$data->parent_id = $input->getUint('parent_id', 0);
$data->json = array();
$data->shortcode = '';
// only if we are creating the shortcode, set the creation date and user
if ($data->id <= 0)
{
$data->createdby = JFactory::getUser()->id;
$data->createdon = JFactory::getDate()->toSql();
}
// get layout path
$path = implode(DIRECTORY_SEPARATOR, array(VBO_SITE_PATH, 'views', $data->type, 'tmpl', 'default.xml'));
// if the file doesn't exist, raise an exception
if (!is_file($path))
{
throw new Exception("Missing XML [{$data->type}] view type.", 404);
}
// load XML form
$form = JForm::getInstance($data->type, $path);
// obtain view title
$data->title = (string) $form->getXml()->layout->attributes()->title;
// get form data
$formData = $input->get('jform', [], 'array');
// get input filter
$inputFilter = JInputFilter::getInstance();
// iterate the layout fields
foreach ($form->getFields() as $field)
{
$attrs = $field->attributes();
$name = (string) $attrs->name;
$filter = (string) $attrs->filter;
$req = (string) $attrs->required;
// use string if no filter
if (empty($filter))
{
$filter = 'string';
}
if (isset($formData[$name]))
{
// clean filter in request
$value = $inputFilter->clean($formData[$name], $filter);
}
else
{
// use NULL
$value = null;
}
// raise an exception if a mandatory field is empty
if (empty($value) && $req == 'true')
{
throw new Exception("Missing required [$name] field.", 400);
}
// save value only if not NULL
if ($value !== null)
{
$data->json[$name] = $value;
}
}
$viewData = array();
$viewData['view'] = $data->type;
$viewData['lang'] = $data->lang;
// merge VIEW name and LANG TAG with JSON params
$args = array_merge($data->json, $viewData);
// generate shortcode string
$data->shortcode = JFilterOutput::shortcode('vikbooking', $args);
// finally encode the params in JSON
$data->json = json_encode($data->json);
return $data;
}
/**
* @override
* Retrieves the specified item.
*
* @param mixed $pk The primary key value or a list of keys.
* @param boolean $create True to create an empty object.
*
* @return object The item found if exists, otherwise an empty object.
*/
public function getItem($pk, $create = true)
{
$item = parent::getItem($pk, $create);
if ($item && !$item->id)
{
$item->name = '';
$item->type = '';
$item->json = '{}';
}
return $item;
}
/**
* @override
* Creates or updates the specified record.
*
* @param object &$data The record to insert.
*
* @return mixed The ID of the inserted record on success, false otherwise.
*/
public function save(&$data)
{
// get old item to get previous shortcode
$old = ($data->id ?? 0) ? $this->getItem($data->id) : null;
// save shortcode
$res = parent::save($data);
if ($res && $old)
{
// get saved item to access post ID property
$item = $this->getItem($data->id);
/**
* Get the post object.
* Obtain post only in case the shortcode is assigned
* to a real ID, otherwise get_post() could retrieve
* the last post used.
*
* @since 1.2.7
*/
$post = $item->post_id ? get_post($item->post_id) : null;
/**
* Proceed only in case the post exists.
*
* @since 1.1.7
*/
if ($post)
{
/**
* Do not proceed in case the post already contains the shortcode.
* Otherwise we would fall in a loop as wp_update_post() triggers
* the action that invoked the current method.
*
* @see vikbooking.php @ action:save_post
*
* @since 1.0.17
*/
if (strpos($post->post_content, $item->shortcode) === false)
{
// replace old shortcode with the new one from the post contents
$post->post_content = str_replace($old->shortcode, $item->shortcode, $post->post_content);
// finalize the update
wp_update_post($post);
}
}
}
return $res;
}
/**
* Obtains the JForm object related to the model view.
*
* @param object $item The data to bind.
*
* @return JForm The form object.
*/
public function getTypeForm($item)
{
$item = (object) $item;
if (!$item->type)
{
return null;
}
// inject custom vars to access the layout
// file of a site view (change model name and client)
$this->_name = $item->type;
$this->_client = 'site';
// get the form
$form = parent::getForm();
$form->setFormControl('jform');
// reset the vars (it will be taken later if needed)
$this->_name = null;
$this->_client = null;
return $form;
}
/**
* Retrieves the IDs of the ancestors of a shortcode.
*
* @param mixed $shortcode Either a shortcode ID or an object.
*
* @return array Array of ancestor IDs or empty array if there are none.
*
* @since 1.5
*/
public function getAncestors($shortcode)
{
if (is_array($shortcode))
{
// treat associative array as object
$shortcode = (object) $shortcode;
}
else if (!is_object($shortcode))
{
// fetch shortcode details from ID
$shortcode = $this->getItem($shortcode);
}
$ancestors = [];
if (!$shortcode || empty($shortcode->parent_id) || $shortcode->parent_id == $shortcode->id)
{
return $ancestors;
}
$id = $shortcode->parent_id;
$ancestors[] = $id;
while ($ancestor = $this->getItem($id))
{
// Loop detection: If the ancestor has been seen before, break
if (empty($ancestor->parent_id) || ($ancestor->parent_id == $shortcode->id ) || in_array($ancestor->parent_id, $ancestors, true))
{
break;
}
$id = $ancestor->parent_id;
$ancestors[] = $id;
}
return $ancestors;
}
/**
* Creates a page on WordPress with for each requested shortcode.
* This is useful to automatically link Shortcodes in pages with no manual actions.
*
* @param mixed $ids Either an array or a shortcode ID.
*
* @return boolean True on success, false otherwise.
*
* @since 1.5
*/
public function addPage($ids)
{
$ids = (array) $ids;
$success = false;
foreach ($ids as $id)
{
// get shortcode record
$item = $this->getItem($id);
if (empty($item->id))
{
// missing shortcode
$this->setError(sprintf('Shortcode [%d] not found', $id));
continue;
}
// make sure the shortcode hasn't been assigned to any pages
if (empty($item->post_id))
{
$post_parent = 0;
if ($item->parent_id)
{
// get parent shortcode
$parent = $this->getItem($item->parent_id);
if ($parent && $parent->post_id)
{
// in case the parent shortcode is assigned to a post, use it as parent
$post_parent = $parent->post_id;
}
}
// Add a new page (we allow a WP_ERROR to be thrown in case of failure).
// This should automatically trigger the hook that we use to link the shortcode
// to the new page/post ID, and so there's no need to update the item.
$post_id = wp_insert_post(array(
'post_title' => (!empty($item->name) ? $item->name : JText::translate($item->title)),
'post_content' => $item->shortcode,
'post_status' => 'publish',
'post_type' => 'page',
'post_parent' => $post_parent,
), $wp_error = false, $fire_after_hooks = true);
// check if we received an error
if ($post_id instanceof WP_Error)
{
// propagate error message
$this->setError($post_id);
continue;
}
// check whether the page has been created
$success = $success || (is_int($post_id) && $post_id > 0);
}
}
return $success;
}
/**
* Method to get a table object.
*
* @param string $name The table name.
* @param string $prefix The class prefix.
* @param array $options Configuration array for table.
*
* @return JTable A table object.
*
* @since 1.4.7
*/
public function getTable($name = '', $prefix = 'JTable', $options = array())
{
if (!$name)
{
$name = 'shortcode';
$prefix = 'VBOTable';
}
return parent::getTable($name, $prefix, $options);
}
}