| Current Path : /home/cbequiperp/www/components/com_flexicontent/ |
| Current File : /home/cbequiperp/www/components/com_flexicontent/controller.php |
<?php
/**
* @package FLEXIcontent
* @version 3.3
*
* @author Emmanuel Danan, Georgios Papadakis, Yannick Berges, others, see contributor page
* @link https://flexicontent.org
* @copyright Copyright © 2018, FLEXIcontent team, All Rights Reserved
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
*/
defined('_JEXEC') or die;
use Joomla\String\StringHelper;
use Joomla\Utilities\ArrayHelper;
jimport('legacy.controller.legacy');
JLoader::register('FlexicontentControllerItems', JPATH_BASE.DS.'components'.DS.'com_flexicontent'.DS.'controllers'.DS.'items.php'); // we use JPATH_BASE since controller exists in frontend too
/**
* FLEXIcontent Component Controller
*
* @package Joomla
* @subpackage FLEXIcontent
* @since 1.0
*/
class FlexicontentController extends JControllerLegacy
{
var $records_dbtbl = 'content';
var $records_jtable = 'flexicontent_items';
var $record_name = 'item';
var $record_name_pl = 'items';
var $_NAME = 'ITEM';
var $record_alias = 'alias';
/**
* Constructor
*
* @param array $config associative array of configuration settings.
*
* @since 3.3
*/
public function __construct($config = array())
{
parent::__construct($config);
/**
* Register task aliases
*/
$this->registerTask('apply_type', 'save');
$this->registerTask('save_a_preview', 'save');
if (JFactory::getApplication()->isClient('site'))
{
$this->registerTask('download_tree', 'download');
$this->registerTask('download_file', 'download');
$this->input = empty($this->input) ? JFactory::getApplication()->input : $this->input;
$this->option = $this->input->get('option', '', 'cmd');
$this->task = $this->input->get('task', '', 'cmd');
$this->view = $this->input->get('view', '', 'cmd');
$this->format = $this->input->get('format', '', 'cmd');
// Get referer URL from HTTP request and validate it
$this->refererURL = !empty($_SERVER['HTTP_REFERER']) && flexicontent_html::is_safe_url($_SERVER['HTTP_REFERER'])
? $_SERVER['HTTP_REFERER']
: JUri::base();
// Get return URL from HTTP request and validate it
$this->returnURL = $this->_getReturnUrl();
// For frontend default return is refererURL
$this->returnURL = $this->returnURL ?: $this->refererURL;
}
else
{
$this->registerTask('unfeatured', 'featured');
$this->registerTask('copy', 'batch');
$this->registerTask('translate', 'batch');
}
}
/**
* Logic to create category SEF urls via AJAX requests
*
* @access public
* @return void
* @since 1.0
*/
function getsefurl()
{
// Initialize variables
$app = JFactory::getApplication();
$db = JFactory::getDbo();
$view = $this->input->get('view', '', 'cmd');
$cid = $this->input->get('cid', 0, 'int');
if ($view=='category' && $cid)
{
$query = 'SELECT CASE WHEN CHAR_LENGTH(c.alias) THEN CONCAT_WS(\':\', c.id, c.alias) ELSE c.id END as categoryslug'
.' FROM #__categories AS c'
.' WHERE c.id = '.$cid;
$db->setQuery( $query );
$categoryslug = $db->loadResult();
echo JRoute::_(FlexicontentHelperRoute::getCategoryRoute($categoryslug), false);
}
jexit();
}
/**
* Display the view
*/
function display($cachable = null, $urlparams = false)
{
$CLIENT_CACHEABLE_PUBLIC = 1;
$CLIENT_CACHEABLE_PRIVATE = 2;
$userid = JFactory::getUser()->get('id');
$cc = $this->input->get('cc', null);
$view = $this->input->get('view', '', 'cmd');
$layout = $this->input->get('layout', '', 'cmd');
// Access checking for --items-- viewing, will be handled by the items model, this is because THIS display() TASK is used by other views too
// in future it maybe moved here to the controller, e.g. create a special task item_display() for item viewing, or insert some IF bellow
// ///////////////////////
// Display case: ITEM FORM
// ///////////////////////
// Also a compatibility check: Layout is form and task is not set: this is new item submit ...
if ( $this->input->get('layout', false) == "form" && !$this->input->get('task', false))
{
$this->input->set('browser_cachable', 0);
$this->input->set('task', 'add');
// The 'add' task does not exist, instead it is an alias to 'edit' task
$this->edit();
return;
}
// //////////////////////////////////////////////////////////////////////////
// Display case: FLEXIcontent frontend view (category, item, favourites, etc)
// //////////////////////////////////////////////////////////////////////////
// *******************
// Handle SERVER Cache
// *******************
// SHOW RECENT FAVOURED ITEMS IMMEDIATELY: do not cache the view
if ($view=='favourites' || ($view=='category' && $layout=='favs')) $cachable = false;
// AVOID MAKING TOO LARGE (case 1): search view or other view with TEXT search active
else if ($view=='search' || $this->input->get('filter', '', 'string')) $cachable = false;
// AVOID MAKING TOO LARGE: (case 2) some field filters are active
else {
$cachable = true;
foreach($_GET as $i => $v) {
if (substr($i, 0, 7) === "filter_") { $cachable = false; break; }
}
}
// ********************
// Handle browser Cache
// ********************
if ( $cc !== null ) {
// Currently our plugin will ignore this and force 'private', because of risk to break 3rd party extensions doing cookie-based content per guest
$browser_cachable = $userid ? $CLIENT_CACHEABLE_PRIVATE : $CLIENT_CACHEABLE_PUBLIC;
} else {
$browser_cachable = 0;
}
// CASE: urlparams were explicitely given
if (!empty($urlparams)) $safeurlparams = & $urlparams;
// CASE: urlparams are empty, use the FULL URL request array (_GET)
else
{
$safeurlparams = array();
// (1) Add menu URL variables
$menu = JFactory::getApplication()->getMenu()->getActive();
if ($menu)
{
// Add menu Itemid to make sure that the menu items with --different-- parameter values, will display differently
$safeurlparams['Itemid'] = 'STRING';
// Add menu's HTTP query variables so that we match the non-SEF URL exactly, thus we create the same cache-ID for both SEF / non-SEF urls (purpose: save some cache space)
foreach($menu->query as $_varname => $_ignore)
{
$safeurlparams[$_varname] = 'STRING';
}
}
// (2) Add real URL variables (GET)
foreach($_GET as $_varname => $_ignore)
{
$safeurlparams[$_varname] = 'STRING';
}
// (3) Add other variables added during Joomla URL routing
// ... ?
/* (redo 1 and 2 but also implement 3) Add any existing URL variables
* 1) menu URL variables
* 2) real URL variables (GET),
* 3) other variables added during Joomla URL routing
* NOTE: we only need variable names, (values are ignored)
*/
foreach($this->input->getArray() as $_varname => $_ignore)
{
$safeurlparams[$_varname] = 'STRING';
}
}
// If component is serving different pages to logged users, this will avoid
// having users seeing same page after login/logout when conservative caching is used
if ( $userid = JFactory::getUser()->get('id') )
{
$this->input->set('__fc_user_id__', $userid);
$safeurlparams['__fc_user_id__'] = 'STRING';
}
$cparams = JComponentHelper::getParams( 'com_flexicontent' );
$use_mobile_layouts = $cparams->get('use_mobile_layouts', 0);
$tabletSameAsDesktop = $cparams->get('force_desktop_layout', 0) == 1;
// If component is serving different pages for mobile devices, this will avoid
// having users seeing the same page regardless of being on desktop or mobile
$mobileDetector = flexicontent_html::getMobileDetector(); //$client = JFactory::getApplication()->client; $isMobile = $client->mobile;
$isMobile = $mobileDetector->isMobile();
$isTablet = $mobileDetector->isTablet();
if ( $use_mobile_layouts && $isMobile && (!$isTablet || !$tabletSameAsDesktop) )
{
$this->input->set('__fc_client__', 'Mobile' );
$safeurlparams['__fc_client__'] = 'STRING';
}
// Moved code for browser's cache control to system plugin to do at the latest possible point
// =0, NOT user brower CACHEABLE
// >0, user browser CACHEABLE, ask browser to store and redisplay it, without revalidating
// *** Intermediary Cache control
// 1 means CACHEABLE, PUBLIC content, proxies can cache: 'Cache-Control:public'
// 2 means CACHEABLE, PRIVATE (logged user) content, proxies must not cache: 'Cache-Control:private'
// null will let default (Joomla website) HTTP headers, e.g. re-validate
$this->input->set('browser_cachable', $browser_cachable);
//echo "cacheable: ".(int)$cachable." - " . print_r($safeurlparams, true) ."<br/>";
parent::display($cachable, $safeurlparams);
}
/**
* Method of the voting without AJAX. Exists for compatibility reasons, since it can be called by Joomla's content vote plugin.
*
* @access public
* @since 1.0
*/
function vote()
{
// Initialize variables
$app = JFactory::getApplication();
$id = $this->input->get('id', 0, 'int');
$cid = $this->input->get('cid', 0, 'int');
$url = $this->input->get('url', '', 'string');
// Check that the given URL variable is 'safe' (allowed), e.g. not an offsite URL
if ( ! $url || ! flexicontent_html::is_safe_url($url) )
{
if ($url)
{
$dolog = JComponentHelper::getParams( 'com_flexicontent' )->get('print_logging_info');
if ( $dolog ) JFactory::getApplication()->enqueueMessage( 'refused redirection to possible unsafe URL: '.$url, 'notice' );
}
global $globalcats;
$Itemid = $this->input->get('Itemid', 0, 'int'); // maintain current menu item if this was given
$url = JRoute::_(FlexicontentHelperRoute::getItemRoute($id, $globalcats[$cid]->slug, $Itemid));
}
// Finally store the vote
$this->input->set('no_ajax', 1);
$this->ajaxvote();
$this->setRedirect($url);
}
/**
* Ajax favourites
*
* @access public
* @since 1.0
*/
function ajaxfav()
{
$app = JFactory::getApplication();
$user = JFactory::getUser();
//$db = JFactory::getDbo();
//$cparams = JComponentHelper::getParams( 'com_flexicontent' );
$id = $this->input->get('id', 0, 'int');
$type = $this->input->get('type', 'item', 'cmd');
if ($type !== 'item' && $type !== 'category')
{
jexit('Type: ' . $type . ' not supported');
}
// Get Favourites field configuration
$favs_field = reset(FlexicontentFields::getFieldsByIds(array(12)));
$favs_field->parameters = new JRegistry($favs_field->attribs);
$usercount = (int) $favs_field->parameters->get('display_favoured_usercount', 0);
$allow_guests_favs = $favs_field->parameters->get('allow_guests_favs', 1);
// Guest user (and allowing favourites for guests via Cookie data is disabled)
if (!$user->id && !$allow_guests_favs)
{
echo 'login';
}
// Guest user does not have DB data, instead use Cookie data
else
{
// Get model of the give type
$model = $this->getModel($type);
if (!$user->id)
{
// Output simple response without counter
/*echo flexicontent_favs::getInstance()->toggleIsFavoured($type, $id, true) < 1
? 'removed'
: 'added';*/
$isfav = flexicontent_favs::getInstance()->toggleIsFavoured($type, $id, true) < 1;
flexicontent_favs::getInstance()->saveState();
}
// Logged user, update DB, adding / removing given id as favoured
else
{
// Toggle favourite
$isfav = $model->getFavoured();
$isfav
? $model->removefav()
: $model->addfav();
}
// Output response for counter (if this has been enabled)
$favs = $model->getFavourites();
echo $isfav
? ($favs && $usercount ? '-' . $favs : 'removed')
: ($favs && $usercount ? '+' . $favs : 'added');
}
// Item favouring changed clean item-related caches
$cache = FLEXIUtilities::getCache($group='', 0);
$cache->clean('com_flexicontent'); // Also clean this (as it contains Joomla frontend view cache)
jexit();
}
/**
* Ajax review form
*
* @access public
* @since 3.0
*/
function getreviewform()
{
$app = JFactory::getApplication();
$user = JFactory::getUser();
$db = JFactory::getDbo();
$html_tagid = $this->input->get('tagid', '', 'cmd');
$content_id = $this->input->get('content_id', 0, 'int');
$review_type = $this->input->get('review_type', 'item', 'cmd');
$errors = array();
/**
* Check for validation failures on posted data
*/
if (!$content_id)
{
$errors[] = 'content_id is zero';
}
if ($review_type !== 'item')
{
$errors[] = 'review_type <> "item" is not yet supported';
}
/**
* Do voting / reviewing permissions check
*/
if (!count($errors))
{
$item = null;
$field = null;
$this->reviewPrepare($content_id, $item, $field, $errors, $_checkSubmit = true);
}
// Check if an error has been encountered
if (count($errors))
{
$result = (object) array(
'error' => 0,
'html' => '
<div class="fc-mssg fc-warning fc-nobgimage">
<button type="button" class="close" data-dismiss="alert">×</button>
' . implode('<br>', $errors) . '
</div>'
);
jexit(json_encode($result));
}
// Load review of a logged user
$review = false;
if ($user->id)
{
$query = $db->getQuery(true)
->select('*')
->from('#__flexicontent_reviews_dev AS r')
->where('r.content_id = ' . (int) $content_id)
->where('r.type = ' . $db->Quote($review_type))
->where('r.user_id = ' . (int) $user->id);
$review = $db->setQuery($query)->loadObject();
}
$layouts_path = null;
/**
* field: 'Voting' field
* item: item or category record
* type: 'item' or 'category'
* review: review record
* html_tagid: HTML tag id of target box
* user => user object (of user submiting the review)
*/
$review_type = 'item';
$result = (object) array(
'html' => '
<form id="fcvote_review_form_' . $item->id . '" name="fcvote_review_form_' . $item->id . '" action="javascript:;">
<input type="hidden" name="review_id" value="'. ($review ? $review->id : '').'" form="fcvote_review_form_' . $item->id . '" />
<input type="hidden" name="content_id" value="' . $item->id . '" />
<input type="hidden" name="review_type" value="' . $review_type . '" />
<table class="fc-form-tbl fcinner">
<tr class="fcvote_review_form_title_row">
<td class="key">
<label class="fc-prop-lbl" for="fcvote_review_form_' . $item->id . '_title">' . JText::_('FLEXI_VOTE_REVIEW_TITLE') . '</label>
</td>
<td>
<input type="text" name="title" size="200"
value="'.htmlspecialchars( ($review ? $review->title : ''), ENT_COMPAT, 'UTF-8' ).'"
id="fcvote_review_form_' . $item->id . '_title"
/>
</td>
</tr>
<tr class="fcvote_review_form_email_row">
<td class="key">
<label class="fc-prop-lbl" for="fcvote_review_form_' . $item->id . '_email">' . JText::_('FLEXI_VOTE_REVIEW_EMAIL') . '</label>
</td>
<td>' . ($user->id ? '<span class=badge>' . $user->email . '</span>' : '
<input required type="text" name="email" size="200"
value="'.htmlspecialchars( ($review ? $review->email : ''), ENT_COMPAT, 'UTF-8' ).'"
id="fcvote_review_form_' . $item->id . '_email"
/>') . '
</td>
</tr>
<tr class="fcvote_review_form_text_row">
<td class="key">
<label class="fc-prop-lbl" for="fcvote_review_form_' . $item->id . '_text">'.JText::_('FLEXI_VOTE_REVIEW_TEXT').'</label>
</td>
<td class="top">
<textarea required name="text" rows="4" cols="200" id="fcvote_review_form_' . $item->id . '_text" >' . ($review ? $review->text : '') . '</textarea>
</td>
</tr>
<tr class="fcvote_review_form_submit_btn_row">
<td class="key"></td>
<td class="top">
<input type="button" class="btn btn-success fcvote_review_form_submit_btn"
onclick="fcvote_submit_review_form(\'' . $html_tagid . '\', this.form); return false;"
value="' . JText::_('FLEXI_VOTE_REVIEW_SUMBIT') . '"
/>
</td>
</tr>
</table>
</form>
');
jexit(json_encode($result));
}
function storereviewform()
{
$app = JFactory::getApplication();
$user = JFactory::getUser();
$db = JFactory::getDbo();
$review_id = $this->input->get('review_id', 0, 'int');
$content_id = $this->input->get('content_id', 0, 'int');
$review_type = $this->input->get('review_type', 'item', 'cmd');
$errors = array();
// Validate title
$title = flexicontent_html::dataFilter($this->input->get('title', '', 'string'), $maxlength=255, 'STRING', 0); // Decode entities, and strip HTML
// Validate email
$email = $user->id ? $user->email : flexicontent_html::dataFilter($this->input->get('email', '', 'string'), $maxlength=255, 'EMAIL', 0); // Validate email
// Validate text
$text = flexicontent_html::dataFilter($this->input->get('text', '', 'string'), $maxlength=10000, 'STRING', 0); // Validate text only: decode entities and strip HTML
/**
* Check for validation failures on posted data
*/
if (!$content_id)
{
$errors[] = 'content_id is zero';
}
if (!$email)
{
$errors[] = 'Email is invalid or empty';
}
elseif (!$user->id)
{
$query = 'SELECT id FROM #__users WHERE email = ' . $db->Quote($email);
$reviewer = $db->setQuery($query)->loadObject();
if ($reviewer)
{
$errors[] = 'Please login';
}
}
if (!$text)
{
$errors[] = 'Text is invalid or empty';
}
if ($review_type !== 'item')
{
$errors[] = 'review_type <> item is not yet supported';
}
/**
* Do voting / reviewing permissions check
*/
if (!count($errors))
{
$item = null;
$field = null;
$this->reviewPrepare($content_id, $item, $field, $errors, $_checkSubmit = true);
}
if (!count($errors))
{
// Get a 'flexicontent_reviews' JTable instance
$review = JTable::getInstance($type = 'flexicontent_reviews', $prefix = '', $config = array());
$review_props = array('content_id' => $content_id, 'user_id' => $user->id, 'type' => $review_type);
if ($review_id)
{
$review_props['id'] = $review_id;
}
// Try to find existing review and delete
if (!$review->load($review_props))
{
$review->reset();
}
$review->content_id = $content_id;
$review->type = $review_type;
$review->title = $title;
$review->email = $user->id ? '' : $email;
$review->user_id = $user->id;
$review->text = $text;
// Save review into DB
if (!$review->store())
{
$errors[] = 'Error storing review : ' . $review->getError();
}
}
// Create success response
if (!count($errors))
{
$mssg = $review_id
? 'Existing review updated'
: 'New review saved';
$result = (object) array(
'error' => 0,
'html' => '
<div class="fc-mssg fc-success fc-nobgimage">
<button type="button" class="close" data-dismiss="alert">×</button>
' . $mssg . '
</div>'
);
}
// Create error response
else
{
$result = (object) array(
'error' => 1,
'html' => '
<div class="fc-mssg fc-warning fc-nobgimage">
<button type="button" class="close" data-dismiss="alert">×</button>
' . implode('<br>', $errors) . '
</div>'
);
}
// Send response to client
jexit(json_encode($result));
}
/**
* Method to do prechecks for loading / saving review forms
*
* @param object $item by reference variable to return the reviewed item
* @param object $field by reference variable to return the voting (reviews) field
* @param array $errors The array of error messages that have occured
*
* @return void
*
* @since 3.3.0
*/
private function reviewPrepare($content_id, & $item = null, & $field = null, $errors = null, $checkSubmit = true)
{
$app = JFactory::getApplication();
$user = JFactory::getUser();
$db = JFactory::getDbo();
/**
* Load content item related to the review
*/
$item = JTable::getInstance($type = 'flexicontent_items', $prefix = '', $config = array());
if (!$item->load($content_id))
{
$errors[] = 'ID: ' . $pk . ': ' . $item->getError();
return;
}
/**
* Do voting / reviewing permissions check
*/
// Get voting field
$query = 'SELECT * FROM #__flexicontent_fields WHERE field_type = ' . $db->Quote('voting');
$field = $db->setQuery($query)->loadObject();
// Load field's configuration together with type-specific field customization
FlexicontentFields::loadFieldConfig($field, $item);
// Load field's language files
JFactory::getLanguage()->load('plg_flexicontent_fields_core', JPATH_ADMINISTRATOR, 'en-GB', true);
JFactory::getLanguage()->load('plg_flexicontent_fields_core', JPATH_ADMINISTRATOR, null, true);
// Get needed parameters
$allow_reviews = (int) $field->parameters->get('allow_reviews', 0);
// Check reviews are allowed
if (!$allow_reviews)
{
$errors[] = 'Reviews are disabled';
}
// Check if user has the ACCESS level required for voting
elseif ($checkSubmit)
{
$aid_arr = $user->getAuthorisedViewLevels();
$acclvl = (int) $field->parameters->get('submit_acclvl', 1);
$has_acclvl = in_array($acclvl, $aid_arr);
// Create no access Redirect Message
if (!$has_acclvl)
{
$logged_no_acc_msg = $field->parameters->get('logged_no_acc_msg', '');
$guest_no_acc_msg = $field->parameters->get('guest_no_acc_msg', '');
$no_acc_msg = $user->id ? $logged_no_acc_msg : $guest_no_acc_msg;
$no_acc_msg = $no_acc_msg ? JText::_($no_acc_msg) : '';
// Message not set create a Default Message
if (!$no_acc_msg)
{
// Find name of required Access Level
$acclvl_name = '';
if ($acclvl)
{
$query = 'SELECT title FROM #__viewlevels as level WHERE level.id = ' . (int) $acclvl;
$acclvl_name = $db->setQuery($query)->loadResult();
if (!$acclvl_name)
{
$acclvl_name = 'Access Level: ' . $acclvl . ' not found / was deleted';
}
}
$no_acc_msg = JText::sprintf( 'FLEXI_NO_ACCESS_TO_VOTE' , $acclvl_name);
}
$errors[] = 'You are not authorized to submit reviews';
}
}
}
/**
* Method for voting (ajax)
*
* @TODO move the query part to the item model
* @access public
* @since 1.5
*/
public function ajaxvote()
{
JLoader::register('FlexicontentControllerReviews', JPATH_BASE.DS.'components'.DS.'com_flexicontent'.DS.'controllers'.DS.'reviews.php');
$rman = new FlexicontentControllerReviews();
$rman->ajaxvote();
}
/**
* Add new Tag from item screen
*
*/
function addtag()
{
// Check for request forgeries
JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));
$name = $this->input->get('name', null, 'string');
$cid = $this->input->get('id', array(0), 'array');
$cid = ArrayHelper::toInteger($cid, array(0));
$cid = (int) $cid[0];
// Check if tag exists (id exists or name exists)
JLoader::register("FlexicontentModelTag", JPATH_ADMINISTRATOR.DS.'components'.DS.'com_flexicontent'.DS.'models'.DS.'tag.php');
$model = new FlexicontentModelTag();
$model->setId($cid);
$tag = $model->getTag($name);
if ($tag && $tag->id)
{
// Since tag was found just output the loaded tag
$id = $model->get('id');
$name = $model->get('name');
echo $id."|".$name;
jexit();
}
if ($cid)
{
echo "0|Tag not found";
jexit();
}
if (!FlexicontentHelperPerm::getPerm()->CanCreateTags)
{
echo "0|".JText::_('FLEXI_NO_AUTH_CREATE_NEW_TAGS');
jexit();
}
// Add the new tag and output it so that it gets loaded by the form
try {
$obj = new stdClass();
$obj->name = $name;
$obj->published = 1;
$result = $model->store($obj);
echo $result
? $model->get('id') . '|' . $model->get('name')
: '0|New tag was not created';
}
catch (Exception $e) {
echo "0|New tag creation failed";
}
jexit();
}
/**
* Logic to change the state of an item
*
* @access public
* @return void
* @since 1.0
*/
function setitemstate()
{
// Helper method also checks access, according to user ACL
flexicontent_html::setitemstate($this, 'json');
}
function call_extfunc()
{
// Helper method also checks access, since each plugin needs to explicitely declare URL callable methods, which methods also implement access checks
flexicontent_ajax::call_extfunc();
}
/**
* Download logic
*
* @access public
* @since 1.0
*/
public function download()
{
// Import and Initialize some joomla API variables
jimport('joomla.filesystem.file');
$app = JFactory::getApplication();
$db = JFactory::getDbo();
$user = JFactory::getUser();
$session = JFactory::getSession();
$cparams = JComponentHelper::getParams( 'com_flexicontent' );
$task = $this->input->get('task', 'download', 'cmd');
$method = $this->input->get('method', 'download', 'cmd');
// Sanity check
if ($method !== 'view' && $method !== 'download')
{
die('unknown download method:' . $method);
}
/**
* Single file download (via HTTP request) or multi-file downloaded (via a folder structure in session or in DB table)
*/
if ($task === 'download_tree')
{
// TODO: maybe move this part in module
$cart_id = $this->input->get('cart_id', 0, 'int');
if (!$cart_id)
{
// Get zTree data and parse JSON string
$tree_var = $this->input->get('tree_var', '', 'string');
if ($session->has($tree_var, 'flexicontent'))
{
$ztree_nodes_json = $session->get($tree_var, false, 'flexicontent');
}
$nodes = json_decode($ztree_nodes_json);
}
else
{
$cart_token = $this->input->get('cart_token', '', 'cmd');
$query = ' SELECT * FROM #__flexicontent_downloads_cart WHERE id='. $cart_id;
$cart = $db->setQuery($query)->loadObject();
if (!$cart)
{
jexit('Cart with ID: ' . $cart_id . ', was not found');
}
$cart_token_matches = $cart_token==$cart->token; // no access will be checked
$nodes = json_decode($cart->json);
}
// Some validation check
if (!is_array($nodes))
{
$app->enqueueMessage("Tree structure is empty or invalid", 'notice');
$this->setRedirect('index.php', '');
return;
}
$app = JFactory::getApplication();
$tmp_ffname = 'fcmd_uid_'.$user->id.'_'.date('Y-m-d__H-i-s');
$targetpath = JPath::clean($app->getCfg('tmp_path') .DS. $tmp_ffname);
$tree_files = $this->_traverseFileTree($nodes, $targetpath);
//echo "<pre>"; print_r($tree_files); jexit();
if ( empty($tree_files) )
{
$app->enqueueMessage("No files selected for download", 'notice');
$this->setRedirect('index.php', '');
return;
}
}
else//if ($task === 'download' || $task === 'download_file')
{
$file_node = new stdClass();
$file_node->fieldid = $this->input->get('fid', 0, 'int');
$file_node->contentid = $this->input->get('cid', 0, 'int');
$file_node->fileid = $this->input->get('id', 0, 'int');
$coupon_id = $this->input->get('conid', 0, 'int');
$coupon_token = $this->input->get('contok', '', 'string');
if ($coupon_id)
{
$_nowDate = 'UTC_TIMESTAMP()';
$_nullDate = $db->Quote($db->getNullDate());
$query = ' SELECT *'
.', CASE WHEN '
.' expire_on = '.$_nullDate.' OR expire_on > '.$_nowDate
.' THEN 0 ELSE 1 END AS has_expired'
.', CASE WHEN '
.' hits_limit = -1 OR hits < hits_limit'
.' THEN 0 ELSE 1 END AS has_reached_limit'
.' FROM #__flexicontent_download_coupons'
.' WHERE id = ' . $coupon_id . ' AND token = ' . $db->Quote($coupon_token)
;
$coupon = $db->setQuery($query)->loadObject();
if ($coupon)
{
$slink_valid_coupon = !$coupon->has_reached_limit && !$coupon->has_expired ;
if ( !$slink_valid_coupon )
{
$query = ' DELETE FROM #__flexicontent_download_coupons WHERE id='. $coupon->id;
$db->setQuery($query)->execute();
}
}
// Set to false to indicate not found, since null (not set) will mean not given
$file_node->coupon = !empty($coupon) ? $coupon : false;
}
$tree_files = array($file_node);
}
/**
* Create and Execute SQL query to retrieve file info
*/
// Create SELECT OR JOIN / AND clauses for checking Access
$access_clauses['select'] = '';
$access_clauses['join'] = '';
$access_clauses['and'] = '';
$using_access = empty($cart_token_matches) && empty($slink_valid_coupon);
if ( $using_access )
{
// note CURRENTLY multi-download feature does not use coupons
$access_clauses = $this->_createFieldItemAccessClause(
$get_select_access = true,
$include_file = ($task === 'download_file' ? 'fileaccess_only' : true)
);
}
/**
* Get file data for all files
*/
$fields_props = array();
$fields_conf = array();
$valid_files = array();
$email_recipients = array();
foreach ($tree_files as $file_node)
{
// Get file variable shortcuts (reforce being int)
$field_id = (int) $file_node->fieldid;
$content_id = (int) $file_node->contentid;
$file_id = (int) $file_node->fileid;
if (!isset($fields_conf[$field_id]))
{
$q = 'SELECT attribs, name, field_type FROM #__flexicontent_fields WHERE id = '.(int) $field_id;
$db->setQuery($q);
$fld = $db->loadObject();
$fields_conf[$field_id] = new JRegistry($fld->attribs);
$fields_props[$field_id] = $fld;
}
$field_type = $fields_props[$field_id]->field_type;
$query = 'SELECT f.id, f.filename, f.filename_original, f.altname, f.secure, f.url, f.hits, f.stamp, f.size'
.($task !== 'download_file'
? ', u.email as item_owner_email' .
', i.title as item_title, i.introtext as item_introtext, i.fulltext as item_fulltext' .
', i.language as item_language, ie.type_id as item_type_id, i.access as item_access' .
// item and current category slugs (for URL in notifications)
', CASE WHEN CHAR_LENGTH(i.alias) THEN CONCAT_WS(\':\', i.id, i.alias) ELSE i.id END as itemslug' .
', CASE WHEN CHAR_LENGTH(c.alias) THEN CONCAT_WS(\':\', c.id, c.alias) ELSE c.id END as catslug'
: '')
. ', dh.id as history_id' // download history
. $access_clauses['select'] // has access
.' FROM #__flexicontent_files AS f '
.($field_type=='file' ? ' LEFT JOIN #__flexicontent_fields_item_relations AS rel ON rel.field_id = '. $field_id : '') // Only check value usage for 'file' field
.($task !== 'download_file' ? ' LEFT JOIN #__flexicontent_fields AS fi ON fi.id = '. $field_id : '')
.($task !== 'download_file' ? ' LEFT JOIN #__content AS i ON i.id = '. $content_id : '')
.($task !== 'download_file' ? ' LEFT JOIN #__categories AS c ON c.id = i.catid' : '')
.($task !== 'download_file' ? ' LEFT JOIN #__flexicontent_items_ext AS ie ON ie.item_id = i.id' : '')
.($task !== 'download_file' ? ' LEFT JOIN #__flexicontent_types AS ty ON ie.type_id = ty.id' : '')
.($task !== 'download_file' ? ' LEFT JOIN #__users AS u ON u.id = i.created_by' : '')
.' LEFT JOIN #__flexicontent_download_history AS dh ON dh.file_id = f.id AND dh.user_id = '. (int)$user->id
. $access_clauses['join']
.' WHERE 1'
.($task !== 'download_file' ? ' AND i.id = ' . $content_id : '')
.($task !== 'download_file' ? ' AND fi.id = ' . $field_id : '')
.' AND f.id = ' . $file_id
.' AND f.published= 1'
. $access_clauses['and']
;
$file = $db->setQuery($query)->loadObject();
//echo "<pre>". print_r($file, true) ."</pre>"; exit;
/**
* Check if file was found AND IF user has required Access Levels
*/
if ( empty($file) || ($using_access && (!$file->has_content_access || !$file->has_field_access || !$file->has_file_access)) )
{
if (empty($file))
{
$msg = JText::_('FLEXI_FDC_FAILED_TO_FIND_DATA'); // Failed to match DB data to the download URL data
}
else
{
$msg = JText::_( 'FLEXI_ALERTNOTAUTH' );
if (!empty($file_node->coupon))
{
if ( $file_node->coupon->has_expired ) $msg .= JText::_('FLEXI_FDC_COUPON_HAS_EXPIRED'); // No access and given coupon has expired
else if ( $file_node->coupon->has_reached_limit ) $msg .= JText::_('FLEXI_FDC_COUPON_REACHED_USAGE_LIMIT'); // No access and given coupon has reached download limit
else $msg = "unreachable code in download coupon handling";
}
else
{
if (isset($file_node->coupon))
{
$msg .= "<br/> <small>".JText::_('FLEXI_FDC_COUPON_NO_LONGER_USABLE')."</small>";
}
// Redirect unlogged user to login
if ($user->guest)
{
$uri = JUri::getInstance();
$return = $uri->toString();
$url = $cparams->get('login_page', 'index.php?option=com_users&view=login');
$return = strtr(base64_encode($return), '+/=', '-_,');
$url .= '&return='.$return; // '&return='.base64_encode($return);
$url .= '&isfcurl=1';
$app->setHeader('status', 403, true);
$app->enqueueMessage(JText::sprintf('FLEXI_LOGIN_TO_ACCESS', $url), 'error');
$app->redirect($url);
}
// Use custom unauthorized page for already logged user
elseif ($cparams->get('unauthorized_page', ''))
{
$app->setHeader('status', 403, true);
$app->redirect($cparams->get('unauthorized_page'));
}
// JDEBUG is OFF, output a simple not authorized message
elseif(!JDEBUG)
{
$app->setHeader('status', 403, true);
$app->enqueueMessage(JText::_('FLEXI_ALERTNOTAUTH_VIEW'), 'error');
$app->redirect('index.php');
}
// JDEBUG is ON, output a detailed message
$msg .= ''
.(!$file->has_content_access ? "<br/><br/> ".JText::_('FLEXI_FDC_NO_ACCESS_TO')
." -- ".JText::_('FLEXI_FDC_CONTENT_CONTAINS')." ".JText::_('FLEXI_FDC_WEBLINK')
."<br/><small>(".JText::_('FLEXI_FDC_CONTENT_EXPLANATION').")</small>"
: '')
.(!$file->has_field_access ? "<br/><br/> ".JText::_('FLEXI_FDC_NO_ACCESS_TO')
." -- ".JText::_('FLEXI_FDC_FIELD_CONTAINS')." ".JText::_('FLEXI_FDC_WEBLINK')
: '')
.(!$file->has_file_access ? "<br/><br/> ".JText::_('FLEXI_FDC_NO_ACCESS_TO') ." -- ".JText::_('FLEXI_FDC_FILE')." " : '')
;
$msg .= "<br/><br/> ". JText::sprintf('FLEXI_FDC_FILE_DATA', $file_id, $content_id, $field_id);
}
// Enqueue the final created message
$app->enqueueMessage($msg, 'warning');
}
// Only abort further execution for single file download
if ($task !== 'download_tree')
{
$this->setRedirect('index.php', '');
return;
}
}
/**
* (for non-URL) Create file path and check file exists
*/
if (!$file->url)
{
$basePath = $file->secure ? COM_FLEXICONTENT_FILEPATH : COM_FLEXICONTENT_MEDIAPATH;
$file->abspath = str_replace(DS, '/', JPath::clean($basePath.DS.$file->filename));
if (!JFile::exists($file->abspath))
{
$msg = JText::_( 'FLEXI_REQUESTED_FILE_DOES_NOT_EXIST_ANYMORE' );
$app->enqueueMessage($msg, 'notice');
// Only abort for single file download
if ($task !== 'download_tree')
{
$this->setRedirect('index.php', '');
return;
}
}
}
else
{
/**
* We may need absolute URL path later use JUri::root() !! for media manager Links
* we may use readfile(Absolute URL) to force download of a URL link !!
*/
$file->abspath = $file->url == 2
? JUri::root() . $file->filename
: $file->filename;
}
/**
* Get item and field JTable records, and then load field's configuration
*/
$item = JTable::getInstance($type = 'flexicontent_items', $prefix = '', $config = array());
$field = JTable::getInstance($type = 'flexicontent_fields', $prefix = '', $config = array());
$item->load($file_node->contentid);
$field->load($file_node->fieldid);
FlexicontentFields::loadFieldConfig($field, $item);
/**
* Trigger pluging event 'onFieldValueAction_FC'
* Import all system plugins, and all FC field plugins
*/
ob_start();
JPluginHelper::importPlugin('system');
JPluginHelper::importPlugin('flexicontent_fields');
$text = ob_get_contents();
ob_end_clean();
// Die on plugin output but ... ignore bogus plugin code adding white characters to output
if (trim($text))
{
die('Aborting, plugin output detected: ' . $text);
}
$value_order = null;
$config = array(
'fileid' => $file_id,
'task' => $task, // (string) 'download', 'download_tree'
'method' => $method, // (string) 'view', 'download'
'coupon_id' => $coupon_id, // int or null
'coupon_token' => $coupon_token // string or null
);
$result = FLEXI_J40GE
? $app->triggerEvent('onFieldValueAction_FC', array(&$field, &$item, $value_order, &$config))
: JEventDispatcher::getInstance()->trigger('onFieldValueAction_FC', array(&$field, &$item, $value_order, &$config));
// Abort on pluging event code returning value -- false --
if ($result === false)
{
// Event should have set the warning / error message ... set none here
$this->setRedirect($this->refererURL);
return;
}
/**
* Increment hits counter of file, and hits counter of file-user history
*/
$filetable = JTable::getInstance('flexicontent_files', '');
$filetable->hit($file_id);
if ( empty($file->history_id) )
{
$query = ' INSERT #__flexicontent_download_history '
. ' SET user_id = ' . (int)$user->id
. ' , file_id = ' . $file_id
. ' , last_hit_on = NOW()'
. ' , hits = 1'
;
}
else
{
$query = ' UPDATE #__flexicontent_download_history '
. ' SET last_hit_on = NOW()'
. ' , hits = hits + 1'
. ' WHERE id = '. (int)$file->history_id
;
}
$db->setQuery($query)->execute();
/**
* Increment hits on download coupon or delete the coupon if it has expired due to date or hits limit
*/
if (!empty($file_node->coupon))
{
if (!$file_node->coupon->has_reached_limit && !$file_node->coupon->has_expired)
{
$query = ' UPDATE #__flexicontent_download_coupons'
.' SET hits = hits + 1'
.' WHERE id='. $file_node->coupon->id
;
$db->setQuery($query)->execute();
}
}
/**
* Special case file is a URL
*/
if ($file->url)
{
// Check and prefix URL Media manager links too
$url = $file->url == 2
? JUri::root(true) . '/' . $file->filename
: $file->filename;
$ext = strtolower(flexicontent_upload::getExt($url));
// Check for empty URL
if (empty($url))
{
$msg = "File URL is empty: ".$file->url;
$app->enqueueMessage($msg, 'error');
return false;
}
// skip url-based file if downloading multiple files
if ($task == 'download_tree')
{
$msg = "Skipped URL based file: ".$url;
$app->enqueueMessage($msg, 'notice');
continue;
}
else
{
$force_url_download = (int) $fields_conf[$field_id]->get('force_url_download', 0);
$force_url_download_exts = preg_split("/[\s]*,[\s]*/", strtolower($fields_conf[$field_id]->get('force_url_download_exts', 'bmp,wbmp,gif,jpg,jpeg,png,webp,ico,wav,mp3,aiff')));
$force_url_download_exts = array_flip($force_url_download_exts);
$force_url_download_max_kbs = (int) $fields_conf[$field_id]->get('force_url_download_max_kbs', 100000);
if ($force_url_download && isset($force_url_download_exts[$ext]))
{
$size = $this->_get_file_size_from_url($url, $retry = true);
if ($size != $file->size)
{
$file->size = $size;
}
}
/**
* Just redirect to the file URL. If force URL download is disabled, or does not match criteria.
* Also do not force is file size is suspiciously small, propably it was calculated correctly !!
*/
if (!$force_url_download || !isset($force_url_download_exts[$ext]) || $file->size < 2048 || $file->size > ($force_url_download_max_kbs * 1024))
{
// Redirect to the file download link
@header("Location: ".$url."","target=blank");
$app->close();
}
}
}
/**
* Set file (tree) node and assign file into valid files for downloading
*/
$file->node = $file_node;
$valid_files[$file_id] = $file;
$file->hits++;
$per_downloads = $fields_conf[$field_id]->get('notifications_step', 20);
// Create current date string according to configuration
$current_date = flexicontent_html::getDateFieldDisplay($fields_conf[$field_id], $_date_ = '', 'stamp_');
$file->header_text = $fields_conf[$field_id]->get('pdf_header_text', '');
$file->footer_text = $fields_conf[$field_id]->get('pdf_footer_text', '');
$result = preg_match_all("/\%\%([^%]+)\%\%/", $file->header_text, $translate_matches);
if (!empty($translate_matches[1])) foreach ($translate_matches[1] as $translate_string)
{
$file->header_text = str_replace('%%'.$translate_string.'%%', JText::_($translate_string), $file->header_text);
}
$file->header_text = str_replace('{{current_date}}', $current_date, $file->header_text);
$result = preg_match_all("/\%\%([^%]+)\%\%/", $file->footer_text, $translate_matches);
if (!empty($translate_matches[1])) foreach ($translate_matches[1] as $translate_string)
{
$file->footer_text = str_replace('%%'.$translate_string.'%%', JText::_($translate_string), $file->footer_text);
}
$file->footer_text = str_replace('{{current_date}}', $current_date, $file->footer_text);
/**
* Send notifications email about file download if file was download via a field that has these notifications enabled
*/
if ($task !== 'download_file' && $fields_conf[$field_id]->get('send_notifications') && ($file->hits % $per_downloads == 0) )
{
// Calculate (once per file) some text used for notifications
$file->__file_title__ = $file->altname && $file->altname != $file->filename
? $file->altname . ' ['.$file->filename.']'
: $file->filename;
$item = new stdClass();
$item->access = $file->item_access;
$item->type_id = $file->item_type_id;
$item->language = $file->item_language;
$file->__item_url__ = JRoute::_(FlexicontentHelperRoute::getItemRoute($file->itemslug, $file->catslug, 0, $item));
// Parse and identify language strings and then make language replacements
$notification_tmpl = $fields_conf[$field_id]->get('notification_tmpl');
if ( empty($notification_tmpl) )
{
$notification_tmpl = JText::_('FLEXI_HITS') .": ".$file->hits;
$notification_tmpl .= '%%FLEXI_FDN_FILE_NO%% __file_id__: "__file_title__" '."\n";
$notification_tmpl .= '%%FLEXI_FDN_FILE_IN_ITEM%% "__item_title__":' ."\n";
$notification_tmpl .= '__item_url__';
}
$result = preg_match_all("/\%\%([^%]+)\%\%/", $notification_tmpl, $translate_matches);
$translate_strings = $result ? $translate_matches[1] : array();
foreach ($translate_strings as $translate_string)
{
$notification_tmpl = str_replace('%%'.$translate_string.'%%', JText::_($translate_string), $notification_tmpl);
}
$file->notification_tmpl = $notification_tmpl;
// Send to hard-coded email list
$send_all_to_email = $fields_conf[$field_id]->get('send_all_to_email');
if ($send_all_to_email)
{
$emails = preg_split("/[\s]*[;,][\s]*/", $send_all_to_email);
foreach($emails as $email)
{
$email_recipients[trim($email)][] = $file;
}
}
// Send to item owner
$send_to_current_item_owner = (int) $fields_conf[$field_id]->get('send_to_current_item_owner');
if ($send_to_current_item_owner)
{
$email_recipients[$file->item_owner_email][] = $file;
}
// Send to email assigned to email field in same content item
$send_to_email_field = (int) $fields_conf[$field_id]->get('send_to_email_field');
if ($send_to_email_field) {
$q = 'SELECT value '
.' FROM #__flexicontent_fields_item_relations '
.' WHERE field_id = ' . $send_to_email_field .' AND item_id='.$content_id;
$db->setQuery($q);
$email_values = $db->loadColumn();
foreach ($email_values as $i => $email_value) {
if ( @unserialize($email_value)!== false || $email_value === 'b:0;' ) {
$email_values[$i] = unserialize($email_value);
} else {
$email_values[$i] = array('addr' => $email_value, 'text' => '');
}
$addr = @ $email_values[$i]['addr'];
if ( $addr ) {
$email_recipients[$addr][] = $file;
}
}
}
}
}
//echo "<pre>". print_r($valid_files, true) ."</pre>";
//echo "<pre>". print_r($email_recipients, true) ."</pre>";
//jexit();
if (!empty($email_recipients))
{
ob_start();
$sendermail = $app->getCfg('mailfrom');
$sendermail = JMailHelper::cleanAddress($sendermail);
$sendername = $app->getCfg('sitename');
$subject = JText::_('FLEXI_FDN_FILE_DOWNLOAD_REPORT');
$message_header = JText::_('FLEXI_FDN_FILE_DOWNLOAD_REPORT_BY') .': '. $user->name .' ['.$user->username .']';
/**
* Send email notifications about file being downloaded
*/
// Personalized email per subscribers
foreach ($email_recipients as $email_addr => $files_arr)
{
$to = JMailHelper::cleanAddress($email_addr);
$_message = $message_header;
foreach($files_arr as $filedata)
{
$_mssg_file = $filedata->notification_tmpl;
$_mssg_file = str_ireplace('__file_id__', $filedata->id, $_mssg_file);
$_mssg_file = str_ireplace('__file_title__', $filedata->__file_title__, $_mssg_file);
$_mssg_file = str_ireplace('__item_title__', $filedata->item_title, $_mssg_file);
//$_mssg_file = str_ireplace('__item_title_linked__', $filedata->password, $_mssg_file);
$_mssg_file = str_ireplace('__item_url__', $filedata->__item_url__, $_mssg_file);
$count = 0;
$_mssg_file = str_ireplace('__file_hits__', $filedata->hits, $_mssg_file, $count);
if ($count == 0) $_mssg_file = JText::_('FLEXI_HITS') .": ".$file->hits ."\n". $_mssg_file;
$_message .= "\n\n" . $_mssg_file;
}
//echo "<pre>". $_message ."</pre>";
$from = $sendermail;
$fromname = $sendername;
$recipient = array($to);
$html_mode=false; $cc=null; $bcc=null;
$attachment=null; $replyto=null; $replytoname=null;
$send_result = JFactory::getMailer()->sendMail( $from, $fromname, $recipient, $subject, $_message, $html_mode, $cc, $bcc, $attachment, $replyto, $replytoname );
}
ob_end_clean();
}
// * Required for IE, otherwise Content-disposition is ignored
if (ini_get('zlib.output_compression'))
{
ini_set('zlib.output_compression', 'Off');
}
// *** Single file download
if ($task != 'download_tree')
{
$dlfile = reset($valid_files);
}
/* Multi-file download, create a compressed archive (e.g. ZIP) to contain them,
* also adding a text file with name and descriptions
* URLs for download-tree should have been skipped above */
else
{
// Create target (top level) folder
JFolder::create($targetpath, 0755);
// Copy Files
foreach ($valid_files as $file) JFile::copy($file->abspath, $file->node->targetpath);
// Create text/html file with ITEM title / descriptions
// TODO replace this with a TEMPLATE file ...
$desc_filename = $targetpath .DS. "_descriptions";
$handle_txt = fopen($desc_filename.".txt", "w");
$handle_htm = fopen($desc_filename.".htm", "w");
fprintf($handle_htm, '
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb" lang="en-gb" dir="ltr" >
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
'
);
foreach ($valid_files as $file) {
fprintf($handle_txt, "%s", $file->item_title."\n\n");
fprintf($handle_txt, "%s", flexicontent_html::striptagsandcut($file->item_introtext) ."\n\n" );
if ( strlen($file->item_fulltext) ) fprintf($handle_txt, "%s", flexicontent_html::striptagsandcut($file->item_fulltext)."\n\n" );
fprintf($handle_htm, "%s", "<h2>".$file->item_title."</h2>");
fprintf($handle_htm, "%s", "<blockquote>".$file->item_introtext."</blockquote><br/>");
if ( strlen($file->item_fulltext) ) fprintf($handle_htm, "%s", "<blockquote>".$file->item_fulltext."</blockquote><br/>");
fprintf($handle_htm, "<hr/><br/>");
}
fclose($handle_txt);
fclose($handle_htm);
// Get file list recursively, and calculate archive filename
$fileslist = JFolder::files($targetpath, '.', $recurse=true, $fullpath=true);
$archivename = $tmp_ffname . '.zip';
$archivepath = JPath::clean( $app->getCfg('tmp_path').DS.$archivename );
/**
* Create the archive
*/
$za = new flexicontent_zip();
$zip_result = $za->open($archivepath, ZipArchive::CREATE);
if ($zip_result !== true)
{
$msg = JText::_('FLEXI_OPERATION_FAILED'). ": compressed archive could not be created";
$app->enqueueMessage($msg, 'notice');
$this->setRedirect('index.php', '');
return;
}
$za->addDir($targetpath, "");
$za->close();
/**
* Remove temporary folder structure
*/
if (!JFolder::delete(($targetpath)) )
{
$msg = "Temporary folder ". $targetpath ." could not be deleted";
$app->enqueueMessage($msg, 'notice');
}
// Delete old files (they can not be deleted during download time ...)
$tmp_path = JPath::clean($app->getCfg('tmp_path'));
$matched_files = JFolder::files($tmp_path, 'fcmd_uid_.*', $recurse=false, $fullpath=true);
foreach ($matched_files as $archive_file)
{
//echo "Seconds passed:". (time() - filemtime($tmp_folder)) ."<br>". "$filename was last modified: " . date ("F d Y H:i:s.", filemtime($tmp_folder)) . "<br>";
if (time() - filemtime($archive_file) > 3600) JFile::delete($archive_file);
}
// Delete old tmp folder (in case that the some archiving procedures were interrupted thus their tmp folder were not deleted)
$matched_folders = JFolder::folders($tmp_path, 'fcmd_uid_.*', $recurse=false, $fullpath=true);
foreach ($matched_folders as $tmp_folder) {
//echo "Seconds passed:". (time() - filemtime($tmp_folder)) ."<br>". "$filename was last modified: " . date ("F d Y H:i:s.", filemtime($tmp_folder)) . "<br>";
JFolder::delete($tmp_folder);
}
$dlfile = new stdClass();
$dlfile->filename = 'cart_files_'.date('m-d-Y_H-i-s'). '.zip'; // a friendly name instead of $archivename
$dlfile->abspath = $archivepath;
}
// Get file filesize and extension
$dlfile->size = !$dlfile->url ? filesize($dlfile->abspath) : $dlfile->size;
$dlfile->ext = strtolower(flexicontent_upload::getExt($dlfile->filename));
// Set content type of file (that is an archive for multi-download)
$ctypes = array(
"pdf" => "application/pdf", "exe" => "application/octet-stream", "rar" => "application/zip", "zip" => "application/zip",
"txt" => "text/plain", "doc" => "application/msword", "xls" => "application/vnd.ms-excel", "ppt" => "application/vnd.ms-powerpoint",
"gif" => "image/gif", "png" => "image/png", "jpeg" => "image/jpg", "jpg" => "image/jpg", "mp3" => "audio/mpeg"
);
$dlfile->ctype = isset($ctypes[$dlfile->ext]) ? $ctypes[$dlfile->ext] : "application/force-download";
if (!$dlfile->url)
{
$dlfile->download_filename = strlen($dlfile->filename_original) ? $dlfile->filename_original : $dlfile->filename;
}
else
{
$_url = strlen($dlfile->filename_original) ? $dlfile->filename_original : $dlfile->filename;
$dlfile->download_filename = strrpos($_url, '/') !== false
? substr($_url, strrpos($_url, '/') + 1)
: $_url;
}
/**
* Handle PDF time-stamping
*/
$pdf = false;
$dlfile->abspath_tmp = false;
$dlfile->size_tmp = false;
// Do not try to stamp URLs
if (!$dlfile->url && $dlfile->ext == 'pdf' && $fields_conf[$field_id]->get('stamp_pdfs', 0) && $dlfile->stamp)
{
// Create new PDF document (initiate FPDI)
$TCPDF_path = JPATH_SITE.DS.'components'.DS.'com_flexicontent'.DS.'librairies'.DS.'TCPDF'.DS.'vendor'.DS.'autoload.php';
if (file_exists($TCPDF_path))
{
$pdf = new flexicontent_FPDI();
$pdf->setAllPagesHeaderText($file->header_text);
$pdf->setAllPagesFooterText($file->footer_text);
$pdf->setHeaderConf(array(
'ffamily' => $fields_conf[$field_id]->get('pdf_header_ffamily', 'Helvetica'),
'fstyle' => $fields_conf[$field_id]->get('pdf_header_fstyle', ''),
'fsize' => $fields_conf[$field_id]->get('pdf_header_fsize', '12'),
'border_type' => $fields_conf[$field_id]->get('pdf_header_border_type', '0'),
'border_width' => 2,
'border_color' => array(0,0,0),
'text_align' => $fields_conf[$field_id]->get('pdf_header_align', 'C')
));
$pdf->setFooterConf(array(
'ffamily' => $fields_conf[$field_id]->get('pdf_footer_ffamily', 'Helvetica'),
'fstyle' => $fields_conf[$field_id]->get('pdf_footer_fstyle', ''),
'fsize' => $fields_conf[$field_id]->get('pdf_footer_fsize', '12'),
'border_type' => $fields_conf[$field_id]->get('pdf_footer_border_type', '0'),
'border_width' => 2,
'border_color' => array(0,0,0),
'text_align' => $fields_conf[$field_id]->get('pdf_footer_align', 'C')
));
// Set the source file
try
{
$pageCount = $pdf->setSourceFile($dlfile->abspath);
//echo '<span>Converting file: <span style="color: darkgreen; font-weight: bold;">'.$dlfile->abspath . '</span></span><br/>';
}
catch (Exception $e)
{
$pdf = false;
//die('<blockquote>Cannot convert file: <span style="color: darkred; font-weight: bold;">'.$dlfile->abspath.'</span> Error: '. $e->getMessage() . '</blockquote>');
}
}
}
// IF $pdf is non-empty, then it was created above
if ($pdf)
{
// Loop through all pages
for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++)
{
// Read next page from source file
$tplIdx = $pdf->importPage($pageNo);
// Create new empty page, and add it to it the imported page, formating it according to our templage
$pdf->AddPage();
$pdf->useTemplate($tplIdx, null, null, 0, 0, true);
}
// Output formatted PDF data into a new file
$tmpDir = ini_get("upload_tmp_dir") ? ini_get("upload_tmp_dir") : sys_get_temp_dir();
$tmpDir .= DIRECTORY_SEPARATOR . "fc_pdf_downloads";
if (!file_exists($tmpDir))
{
if (@ !mkdir($tmpDir) ) die('Can not create temporary folder for handling PDF file');
}
$pdf_filename = basename($dlfile->abspath);
$dlfile->abspath_tmp = $tmpDir . DIRECTORY_SEPARATOR . date('Y_m_d_').uniqid() . '_' . $pdf_filename;
$pdf->Output($dlfile->abspath_tmp, "F");
$dlfile->size_tmp = filesize($dlfile->abspath_tmp);
// Output formatted PDF data to stdout (this browser if running via web-server, we would probably want to set HTTP headers first)
//$pdf->Output();
//die('is PDF: ' . $dlfile->abspath);
}
//echo '<pre>'; print_r($dlfile); exit;
// *****************************************
// Output an appropriate Content-Type header
// *****************************************
header("Pragma: public"); // required
header("Expires: 0");
//header("HTTP/1.1 200 OK");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false); // required for certain browsers
header("Content-Type: ".$dlfile->ctype);
// Set desired filename when downloading, quoting it to allow spaces in filenames
$method == 'view'
? header("Content-Disposition: inline; filename=\"".$dlfile->download_filename."\";" )
: header("Content-Disposition: attachment; filename=\"".$dlfile->download_filename."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . ($dlfile->size_tmp ?: $dlfile->size));
// *******************************
// Finally read file and output it
// *******************************
if ( !FLEXIUtilities::funcIsDisabled('set_time_limit') ) @set_time_limit(0);
$chunksize = 1 * (1024 * 1024); // 1MB, highest possible for fread should be 8MB
$filesize = $dlfile->size_tmp ?: $dlfile->size;
// Do not try to read too big file URL files
if ($dlfile->url && $filesize > 100 * (1024 * 1024))
{
@header("Location: ".$url."","target=blank");
$app->close();
}
elseif ($filesize > $chunksize)
{
$handle = @fopen($dlfile->abspath_tmp ?: $dlfile->abspath, 'rb');
// Redirect to the exteral file download link if we failed to open the URL
if (!$handle && $dlfile->url)
{
@header("Location: ".$url."","target=blank");
$app->close();
}
while(!feof($handle))
{
print(@fread($handle, $chunksize));
ob_flush();
flush();
}
fclose($handle);
}
else
{
// This is good for small files, it will read an output the file into
// memory and output it, it will cause a memory exhausted error on large files
ob_clean();
flush();
readfile($dlfile->abspath_tmp ?: $dlfile->abspath);
}
// ****************************************************
// In case of multi-download clear the session variable
// ****************************************************
//if ($task=='download_tree') $session->set($tree_var, false,'flexicontent');
// Done ... terminate execution
$app->close();
}
/**
* External link logic
*
* @access public
* @since 1.5
*/
function weblink()
{
// Import and Initialize some joomla API variables
$app = JFactory::getApplication();
$db = JFactory::getDbo();
$user = JFactory::getUser();
// Get HTTP REQUEST variables
$field_id = $this->input->get('fid', 0, 'int');
$content_id = $this->input->get('cid', 0, 'int');
$value_order = $this->input->get('ord', 0, 'int');
/**
* Create and Execute SQL query to retrieve file info
*/
// Create SELECT OR JOIN / AND clauses for checking Access
$access_clauses['select'] = '';
$access_clauses['join'] = '';
$access_clauses['and'] = '';
$access_clauses = $this->_createFieldItemAccessClause( $get_select_access = true, $include_file = false );
$query = 'SELECT value'
. $access_clauses['select']
.' FROM #__flexicontent_fields_item_relations AS rel'
.' LEFT JOIN #__flexicontent_fields AS fi ON fi.id = rel.field_id'
.' LEFT JOIN #__content AS i ON i.id = rel.item_id'
.' LEFT JOIN #__categories AS c ON c.id = i.catid'
.' LEFT JOIN #__flexicontent_items_ext AS ie ON ie.item_id = i.id'
.' LEFT JOIN #__flexicontent_types AS ty ON ie.type_id = ty.id'
. $access_clauses['join']
.' WHERE rel.item_id = ' . $content_id
.' AND rel.field_id = ' . $field_id
.' AND rel.valueorder = ' . $value_order
. $access_clauses['and']
;
$link_data = $db->setQuery($query)->loadObject();
/**
* Check if web link value was found AND IF user has required Access Levels
*/
if ( empty($link_data) || (!$link_data->has_content_access || !$link_data->has_field_access) )
{
if (empty($link_data))
{
$msg = JText::_('FLEXI_FDC_FAILED_TO_FIND_DATA'); // Failed to match DB data to the download URL data
}
else
{
// Redirect unlogged user to login
if ($user->guest)
{
$uri = JUri::getInstance();
$return = $uri->toString();
$url = $cparams->get('login_page', 'index.php?option=com_users&view=login');
$return = strtr(base64_encode($return), '+/=', '-_,');
$url .= '&return='.$return; // '&return='.base64_encode($return);
$url .= '&isfcurl=1';
$app->setHeader('status', 403, true);
$app->enqueueMessage(JText::sprintf('FLEXI_LOGIN_TO_ACCESS', $url), 'error');
$app->redirect($url);
}
// Use custom unauthorized page for already logged user
elseif ($cparams->get('unauthorized_page', ''))
{
$app->setHeader('status', 403, true);
$app->redirect($cparams->get('unauthorized_page'));
}
// JDEBUG is OFF, output a simple not authorized message
elseif(!JDEBUG)
{
$app->setHeader('status', 403, true);
$app->enqueueMessage(JText::_('FLEXI_ALERTNOTAUTH_VIEW'), 'error');
$app->redirect('index.php');
}
// JDEBUG is ON, output a detailed message
$msg = JText::_('FLEXI_ALERTNOTAUTH');
$msg .= ""
.(!$link_data->has_content_access ? "<br/><br/> ".JText::_('FLEXI_FDC_NO_ACCESS_TO')
." -- ".JText::_('FLEXI_FDC_CONTENT_CONTAINS')." ".JText::_('FLEXI_FDC_WEBLINK')
."<br/><small>(".JText::_('FLEXI_FDC_CONTENT_EXPLANATION').")</small>"
: '')
.(!$link_data->has_field_access ? "<br/><br/> ".JText::_('FLEXI_FDC_NO_ACCESS_TO')
." -- ".JText::_('FLEXI_FDC_FIELD_CONTAINS')." ".JText::_('FLEXI_FDC_WEBLINK')
: '')
;
$msg .= "<br/><br/> ". JText::sprintf('FLEXI_FDC_WEBLINK_DATA', $value_order, $content_id, $field_id);
}
// Enqueue the final created message
$app->enqueueMessage($msg, 'warning');
// Abort further execution, and redirect to home
$this->setRedirect('index.php', '');
return;
}
/**
* Get item and field JTable records, and then load field's configuration
*/
$item = JTable::getInstance($type = 'flexicontent_items', $prefix = '', $config = array());
$field = JTable::getInstance($type = 'flexicontent_fields', $prefix = '', $config = array());
$item->load($content_id);
$field->load($field_id);
FlexicontentFields::loadFieldConfig($field, $item);
/**
* Trigger pluging event 'onFieldValueAction_FC'
* Import all system plugins, and all FC field plugins
*/
ob_start();
JPluginHelper::importPlugin('system');
JPluginHelper::importPlugin('flexicontent_fields');
$text = ob_get_contents();
ob_end_clean();
// Die on plugin output but ... ignore bogus plugin code adding white characters to output
if (trim($text))
{
die('Aborting, plugin output detected: ' . $text);
}
$config = array(
'task' => 'default'
);
$result = FLEXI_J40GE
? $app->triggerEvent('onFieldValueAction_FC', array(&$field, &$item, $value_order, &$config))
: JEventDispatcher::getInstance()->trigger('onFieldValueAction_FC', array(&$field, &$item, $value_order, &$config));
// Abort on pluging event code returning value -- false --
if ($result === false)
{
// Event should have set the warning / error message ... set none here
$this->setRedirect($this->refererURL);
return;
}
/*
* Increment hits counter
*/
// Recover the link array (url|title|hits)
$link = unserialize($link_data->value);
// Force an absolute URL, if relative URL prepend Joomla root uri
$url = flexicontent_html::make_absolute_url($link['link']);
// Update the hit count
$link['hits'] = (int) $link['hits'] + 1;
$value = serialize($link);
// Update the array in the DB
$query = 'UPDATE #__flexicontent_fields_item_relations'
.' SET value = ' . $db->Quote($value)
.' WHERE item_id = ' . $content_id
.' AND field_id = ' . $field_id
.' AND valueorder = ' . $value_order
;
$db->setQuery($query)->execute();
/**
* Finally redirect to the URL
*/
@header("Location: ".$url."","target=blank");
$app->close();
}
/**
* Method to fetch the tags for selecting in item form
*
* @since 1.5
*/
function viewtags()
{
// Check for request forgeries
JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));
$app = JFactory::getApplication();
$perms = FlexicontentHelperPerm::getPerm();
@ob_end_clean();
//header('Content-type: application/json; charset=utf-8');
header('Content-type: application/json');
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-cache");
header("Pragma: no-cache");
$array = array();
if (!$perms->CanUseTags)
{
$array[] = (object) array(
'id' => '0',
'name' => JText::_('FLEXI_FIELD_NO_ACCESS')
);
}
else
{
$q = $this->input->getString('q', '');
$q = $q !== parse_url(@$_SERVER["REQUEST_URI"], PHP_URL_PATH) ? $q : '';
$model = $this->getModel($this->record_name);
$tagobjs = $model->gettags($q);
if ($tagobjs)
{
foreach ($tagobjs as $tag)
{
$array[] = (object) array(
'id' => $tag->id,
'name' => $tag->name
);
}
}
if (empty($array))
{
$array[] = (object) array(
'id' => '0',
'name' => JText::_($perms->CanCreateTags ? 'FLEXI_NEW_TAG_ENTER_TO_CREATE' : 'FLEXI_NO_TAGS_FOUND')
);
}
}
jexit(json_encode($array/*, JSON_UNESCAPED_UNICODE*/));
}
function search()
{
$app = JFactory::getApplication();
// Strip characters that will cause errors
$badchars = array('#','>','<','\\');
$q = $this->input->getString('q', '');
$q = $q !== parse_url(@$_SERVER["REQUEST_URI"], PHP_URL_PATH) ? $q : '';
$searchword = $this->input->getString('searchword', $q);
$searchword = trim( str_replace($badchars, '', $searchword) );
// If searchword is enclosed in double quotes, then strip quotes and do exact phrase matching
if (substr($searchword,0,1) == '"' && substr($searchword, -1) == '"')
{
$searchword = substr($searchword,1,-1);
$this->input->set('p', 'exact');
$this->input->set('searchphrase', 'exact');
$this->input->set('q', $searchword);
$this->input->set('searchword', $searchword);
}
// If no current menu itemid, then set it using the first menu item that points to the search view
$Itemid = $this->input->get('Itemid', 0, 'int');
if (!$Itemid)
{
$menus = JFactory::getApplication()->getMenu();
$items = $menus->getItems('link', 'index.php?option=com_flexicontent&view=search');
if(isset($items[0]))
{
$this->input->set('Itemid', $items[0]->id);
}
}
// Go through display task of this controller instead of parent class, so that cacheable and safeurlparams can be decided properly
$this->input->set('view', 'search');
$this->display();
}
// **************
// Helper methods
// **************
/**
* Method to create join + and-where SQL CLAUSEs, for checking access of field - item pair(s), IN FUTURE maybe moved
*
* @access private
* @since 1.0
*/
protected function _createFieldItemAccessClause($get_select_access = false, $include_file = false )
{
$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');
$user = JFactory::getUser();
$select_access = $joinacc = $andacc = '';
$aid_arr = $user->getAuthorisedViewLevels();
$aid_list = implode(',', $aid_arr);
// Access Flags for: content item and field
if ($get_select_access)
{
if ($include_file)
{
$select_access .= ', CASE WHEN'.
' f.access IN (0,'.$aid_list.') THEN 1 ELSE 0 END AS has_file_access';
}
if ($include_file === 'fileaccess_only')
{
$select_access .= ', 1 AS has_field_access, 1 AS has_content_access';
}
else
{
$select_access .= ', CASE WHEN'.
' fi.access IN (0,'.$aid_list.') THEN 1 ELSE 0 END AS has_field_access';
$select_access .= ', CASE WHEN'.
' ty.access IN (0,'.$aid_list.') AND '.
' c.access IN (0,'.$aid_list.') AND '.
' i.access IN (0,'.$aid_list.')'.
' THEN 1 ELSE 0 END AS has_content_access';
}
}
else
{
if ($include_file)
{
$andacc .= ' AND f.access IN (0,'.$aid_list.')'; // AND file access
}
if ($include_file !== 'fileaccess_only')
{
$andacc .= ' AND fi.access IN (0,'.$aid_list.')'; // AND field access
$andacc .= ' AND ty.access IN (0,'.$aid_list.') AND c.access IN (0,'.$aid_list.') AND i.access IN (0,'.$aid_list.')'; // AND content access
}
}
$clauses['select'] = $select_access;
$clauses['join'] = $joinacc;
$clauses['and'] = $andacc;
return $clauses;
}
/**
* Traverse Tree to create folder structure and get/prepare file objects
*
* @access private
* @since 1.0
*/
protected function _traverseFileTree($nodes, $targetpath)
{
$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');
jimport('joomla.filesystem.file');
$all_files = array();
foreach ($nodes as $node)
{
// Folder (Parent node)
if ($node->isParent)
{
$targetpath_node = JPath::clean($targetpath.DS.$node->name);
JFolder::create($targetpath_node, 0755);
// Folder has sub-contents
if (!empty($node->children))
{
$node_files = $this->_traverseFileTree($node->children, $targetpath_node);
foreach ($node_files as $nodeID => $file) $all_files[$nodeID] = $file;
}
}
// File (Leaf node)
else
{
$file = new stdClass();
$nodeID = $node->id;
$file->fieldid = (int) $node->fieldid; // sql security ...
$file->contentid = (int) $node->contentid; // sql security ...
$file->fileid = (int) $node->fileid; // sql security ...
$file->filename = $node->name;
// (of course) for each file the target path includes the filename,
// which can be different than original filename (user may have renamed it)
$file->targetpath = $targetpath.DS.$file->filename;
$all_files[$nodeID] = $file;
}
}
return $all_files;
}
/**
* Method to clean cache of a specific record (if implemented by the model)
*
* @since 3.2.1.9
*/
private function _cleanRecordsCache($cid)
{
$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');
// Clean this as it contains Joomla frontend view cache of the component)
$cache_site = FLEXIUtilities::getCache($group = '', $client = 0);
$cache_site->clean('com_flexicontent');
// Also pass item IDs array in case of doing special cache cleaning per item
$itemmodel = $this->getModel($this->record_name);
$itemmodel->cleanCache(null, 0, $cid);
$itemmodel->cleanCache(null, 1, $cid);
}
/**
* Get return URL via a client request variable, checking if it is safe (otherwise home page will be used)
*
* @return string A validated URL to be used typical as redirect URL when a task completes
*
* @since 3.3
*/
protected function _getReturnUrl()
{
$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');
// Get HTTP request variable 'return' (base64 encoded)
$return = $this->input->get('return', null, 'base64');
// Base64 decode the return URL
if ($return)
{
$return = base64_decode($return);
}
// Also try 'referer' (form posted, encode with htmlspecialchars)
else
{
$referer = $this->input->getString('referer', null);
$return = $referer ? htmlspecialchars_decode($referer) : null;
}
// Check return URL if empty or not safe and set a default one
if (empty($return) || !flexicontent_html::is_safe_url($return))
{
$app = JFactory::getApplication();
if ($app->isClient('administrator') && ($this->view === $this->record_name || $this->view === $this->record_name_pl))
{
$return = 'index.php?option=com_flexicontent&view=' . $this->record_name_pl;
}
else
{
$return = $app->isClient('administrator') ? 'index.php?option=com_flexicontent' : JUri::base();
}
}
return $return;
}
/**
* Method to create a query object for getting record data (specific columns) of multiple records
*
* @param array $cid an array record ids
* @param array $cid an array columns names
*
* @return object return a Joomla Database Query object
*
* @since 3.3.0
*/
protected function _getRecordsQuery($cid, $cols)
{
$db = JFactory::getDbo();
$cid = ArrayHelper::toInteger($cid);
$cols_list = implode(',', array_filter($cols, array($db, 'quoteName')));
$query = $db->getQuery(true)
->select($cols_list)
->from('#__' . $this->records_dbtbl)
->where('id IN (' . implode(',', $cid) . ')');
return $query;
}
/**
* START OF CONTROLLER SPECIFIC METHODS
*/
/**
* Returns the size of a file without downloading it, or -1 if the file size could not be determined.
*
* @param $url - The location of the remote file to download. Cannot be null or empty.
*
* @return The size of the file referenced by $url,
* or -1 if the size could not be determined
* or -999 if there was an error
*/
private function _get_file_size_from_url($url, $retry = true)
{
$original_url = $url;
$retry = $retry === true ? 6 : 0;
// clear last error
$ignore_last_error = error_get_last();
try {
$headers = array('Location' => $url);
// Follow the Location headers until the actual file URL is known
while (isset($headers['Location']))
{
$url = is_array($headers['Location'])
? end($headers['Location'])
: $headers['Location'];
$headers = @ get_headers($url, 1);
// Check for get headers failing to execute
if ($headers === false)
{
$error = error_get_last();
$error_message = is_array($error) && isset($error['message'])
? $error['message']
: 'Error retrieving headers of URL';
$this->setError($error_message);
return -999;
}
// Check for bad response from server, e.g. not found 404 , or 403 no access
$n = 0;
while(isset($headers[$n]))
{
$code = (int) substr($headers[$n], 9, 3);
if ($code < 200 || $code >= 400 )
{
$this->setError($headers[$n]);
return -999;
}
$n++;
}
}
}
catch (RuntimeException $e) {
$this->setError($e->getMessage());
return -999; // indicate a fatal error
}
// Work-around with content length missing during 1st try, just retry once more
if (!isset($headers["Content-Length"]) && $retry)
{
return $this->get_file_size_from_url($original_url, --$retry);
}
$headers["Content-Length"] = is_array($headers["Content-Length"]) ? end($headers["Content-Length"]) : $headers["Content-Length"];
// Get file size, -1 indicates that the size could not be determined
return isset($headers["Content-Length"])
? $headers["Content-Length"]
: -1;
}
}