@copyright Copyright (C) 2021. All Rights Reserved. @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) .-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( \____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) /------------------------------------------------------------------------------------------------------*/ // No direct access to this file defined('_JEXEC') or die('Restricted access'); use Joomla\CMS\MVC\Controller\FormController; use Joomla\Utilities\ArrayHelper; /** * Extension Form Controller */ class ExtensiondistributorControllerExtension extends FormController { /** * Current or most recently performed task. * * @var string * @since 12.2 * @note Replaces _task. */ protected $task; /** * Class constructor. * * @param array $config A named array of configuration variables. * * @since 1.6 */ public function __construct($config = array()) { $this->view_list = 'Extensions'; // safeguard for setting the return view listing to the main view. parent::__construct($config); } public function download() { JSession::checkToken('get') or jexit('Wrong token'); global $cparams; $app = JFactory::getApplication(); $extensionId = (int) $app->input->get('extension'); $packageId = (int) $app->input->get('package'); $releaseId = (int) $app->input->get('release'); // We check if a release ID was provided or we get the latest one if ($releaseId) { $latest = new stdClass(); $latest->file = ExtensiondistributorHelper::getVar( 'release', $releaseId, 'id', 'file' ); } else { if ($extensionId > 0) { $latest = ExtensiondistributorHelper::getReleases( $extensionId, 'extension', true ); } else { $latest = ExtensiondistributorHelper::getReleases( $packageId, 'package', true ); } } $folder = $cparams->get('releases_directory'); $filename = $latest->file; $folder = JPath::clean(JPATH_SITE . '/' . $folder . '/'); $file = $folder . $filename; $size = filesize($file); header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header("Content-Transfer-Encoding: Binary"); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); header('Content-Length: ' . $size); ob_clean(); flush(); readfile($file); jexit(); } public function uploadFile() { JSession::checkToken('get') or jexit('Wrong token'); $app = JFactory::getApplication(); global $cparams; $release = $cparams->get('releases_directory', 'packages'); $jcb = $cparams->get('jcb_directory', 'jcb'); $import = $cparams->get('import_directory', 'importfolder'); $type = $app->input->get('type', 'release'); $path = JPath::clean(JPATH_SITE . '/' . ${$type}); $filename = ''; $userfile = array(); foreach ($_FILES as $name => $file) { foreach ($file as $property => $keys) { foreach ($keys as $key => $value) { $userfile[$name][$key][$property] = $value; } } } $userfile = $userfile['files'][0]; $model = $this->getModel('extension'); $package = $model->uploadFile($userfile); // Cette partie est empruntée au com_media de Joomla $extension = strtolower($package['format']); $filename = JFile::makeSafe($package['packagename']); $filename = str_replace('.' . $package['format'], '', $filename); $filename = JStringPunycode::toPunycode($filename); $filename = preg_replace( array("/[\\s]/", '/[^a-zA-Z0-9_\-]/'), array('_', ''), $filename ) . '.' . $extension; // Fin de l'emprunt // move packages to the correct directory $final_dest = $path . DIRECTORY_SEPARATOR . $filename; JFile::move($package['dir'], $final_dest); echo json_encode($package); exit(); } public function getImportFilesList() { JSession::checkToken('get') or jexit('Wrong token'); global $cparams; $app = JFactory::getApplication(); $import = $cparams->get('import_directory', 'importfolder'); $path = JPath::clean(JPATH_SITE . '/' . $import); $files = JFolder::files($path, '.zip', false, true); echo JLayoutHelper::render('importfiles', array($files)); exit(); } public function importExtensions() { JSession::checkToken('get') or jexit('Wrong token'); global $cparams; $app = JFactory::getApplication(); $folder = JPATH_SITE . '/' . $cparams->get( 'import_directory', 'extmanagerimport' ); $category = $cparams->get('catid'); $file = $this->input->get('file', null, 'base64'); if (!is_null($file)) { $files[] = base64_decode($file); } else { $files = JFolder::files($folder, '.zip', false, true); } // Check if there is a return value $return = $this->input->get('return', null, 'base64'); // Preset the redirect if (!is_null($return) && JUri::isInternal(base64_decode($return))) { $redirect = base64_decode($return); // Redirect to the return value. $this->setRedirect( JRoute::_( $redirect, false ) ); } if ($category === null) { $app->enqueueMessage( 'You have to choose the import category in the component configuration', 'error' ); return false; } if (ExtensiondistributorHelper::checkArray($files)) { foreach ($files as $i => $file) { $this->importExtension($file); } } return true; } public function importExtension($file) { global $cparams; $app = JFactory::getApplication(); $packages = $cparams->get('releases_directory', 'packages'); $packages_path = JPath::clean(JPATH_SITE . '/' . $packages . '/'); $folder = JPATH_SITE . '/' . $cparams->get( 'import_directory', 'extmanagerimport' ); $category = $cparams->get('catid'); $icon_failed = ' '; $icon_success = ' '; $success = true; // First extract zip file $zip = new ZipArchive(); $res = $zip->open($file); if ($res === true) { $zip->extractTo($folder . '/tmp'); $zip->close(); $msg[] = sprintf( 'File %s successfully extracted', pathinfo($file)['filename'] ); } else { $msg[] = sprintf('Cannot extract file %s'); } // Check if there's an xml file in the tmp folder $manifest = JFolder::files($folder . '/tmp', '.xml', false, true); if (ExtensiondistributorHelper::checkArray($manifest)) { $extfolder = $folder . '/tmp/'; $manifest = $manifest[0]; } else { // Should be in a subfolder if (JFolder::exists($folder . '/tmp/__MACOSX')) { // Have to find something more global JFolder::delete($folder . '/tmp/__MACOSX'); } // Find the new folder $extracted = JFolder::folders($folder . '/tmp'); $extracted = $extracted[0]; $extfolder = $folder . '/tmp/' . $extracted; // Get the manifest $manifest = JFolder::files($extfolder, '.xml', false, true); $manifest = $manifest[0]; } // Try to find a language directory in the install folder $language_dir = JFolder::folders($extfolder, 'lang(.*)', true, true); if (ExtensiondistributorHelper::checkArray($language_dir)) { // Find a file $language_file = JFolder::files($language_dir[0], '.', true); if (ExtensiondistributorHelper::checkArray($language_file)) { $language_file = $language_file[0]; $ext = explode('.', $language_file); // Now we can load the extension language file $language = JFactory::getLanguage(); $ext = $ext[1]; $base_dir = str_replace('/language', '', $language_dir[0]); $language_tag = null; $reload = true; $language->load($ext, $base_dir, $language_tag, $reload); } } // Read the XML data $xml = simplexml_load_file($manifest); $extension = array(); $extension['type'] = (string) $xml['type']; $extension['group'] = (string) $xml['group']; $extension['client'] = (string) $xml['client']; $extension['name'] = JText::_((string) $xml->name); $extension['description'] = JText::_((string) $xml->description); if ($extension['type'] === 'plugin') { $extension['element'] = (string) $xml->files->filename['plugin']; } else { $extension['element'] = strtolower((string) $xml->name); } $release = array(); $release['version_number'] = (string) $xml->version; try { $reldate = JFactory::getDate((string) $xml->creationDate)->toSql(); } catch (Exception $e) { $reldate = JFactory::getDate()->toSql(); } $release['release_date'] = $reldate; $release['file'] = pathinfo($file)['basename']; // We check if the extension already exists $extensionId = ExtensiondistributorHelper::isExtension( $extension['type'], $extension['element'], $extension['group'], $extension['client'] ); if (!$extensionId) { // If not we create it $model = ExtensiondistributorHelper::getModel('extension'); $element = $model->getItem(); // Extension $element->name = $extension['name']; $element->type = $extension['type']; $element->element = $extension['element']; $element->group = $extension['group']; $element->client = $extension['client']; $element->description = $extension['description']; $element->keepfilename = $cparams->get('keepfilename') ? 1 : 0; $element->newfilename = $cparams->get('newfilename'); $element->created_by = JFactory::getUser()->id; $element->created = JFactory::getDate()->toSql(); $element->alias = JFilterOutput::stringURLSafe( $element->name ); $element->update = JUri::root() . 'updates/' . $element->alias . '.xml'; $element->catid = $category; // Release $element->version_number = $release['version_number']; $element->release_date = $release['release_date']; unset($element->tags); if ($model->save((array) $element)) { $extensionId = ExtensiondistributorHelper::extensionsLastId(); $msg[] = sprintf( '%sExtension %s successfully created | id:%s', $icon_success, $element->name, $extensionId ); } } else { $name = ExtensiondistributorHelper::getVar( 'extension', $extensionId, 'id', 'name' ); $msg[] = sprintf( '%sExtension %s already exists | id:%s', $icon_failed, $name, $extensionId ); } // We check if the release already exists $releaseId = ExtensiondistributorHelper::isRelease( $extensionId, $release['version_number'] ); if (!$releaseId) { // If not we create it $model = ExtensiondistributorHelper::getModel('release'); $element = $model->getItem(); $element->extension = $extensionId; $element->version_number = $release['version_number']; $element->release_date = $release['release_date']; $element->stability = $cparams->get('stability', 4); $element->toolbar = 'NOPE'; $element->zip_content = 'EMPTY'; $element->reltype = $cparams->get('reltype', 'maintenance'); $element->created_by = JFactory::getUser()->id; $element->created = JFactory::getDate()->toSql(); $element->file = $release['file']; if (!$cparams->get('keepfilename')) { // We create the new filename $extensionObject = ExtensiondistributorHelper::getExtensionObject( $extensionId ); $filename = ExtensiondistributorHelper::createExtensionFileName( $extensionObject, $element->version_number, $element->stability, $element->file ); $element->file = $filename; } unset($element->tags); if ($model->save((array) $element)) { $msg[] = sprintf( '%sRelease %s successfully created', $icon_success, $element->version_number ); // We move the releases file to its destination JFile::move($file, $packages_path . $element->file); // We delete the original file if (JFile::exists($file)) { JFile::delete($file); } } } else { $version = ExtensiondistributorHelper::getVar( 'release', $releaseId, 'id', 'version_number' ); $msg[] = sprintf( '%sThe release %s exists already | id:%s', $icon_failed, $version, $releaseId ); $success = false; } $app->enqueueMessage( implode('
', $msg), $success ? 'message' : 'error' ); // We delete the temp folder JFolder::delete($folder . '/tmp'); } public function deleteImportFiles() { JSession::checkToken('get') or jexit('Wrong token'); global $cparams; $app = JFactory::getApplication(); $folder = JPATH_SITE . '/' . $cparams->get( 'import_directory', 'extmanagerimport' ); $file = $this->input->get('file', null, 'base64'); if (!is_null($file)) { $files[] = base64_decode($file); } else { $files = JFolder::files($folder, '.zip', false, true); } // Check if there is a return value $return = $this->input->get('return', null, 'base64'); // Preset the redirect if (!is_null($return) && JUri::isInternal(base64_decode($return))) { $redirect = base64_decode($return); // Redirect to the return value. $this->setRedirect( JRoute::_( $redirect, false ) ); } if (ExtensiondistributorHelper::checkArray($files)) { foreach ($files as $file) { if (JFile::delete($file)) { $app->enqueueMessage( sprintf( 'The file %s was successfully deleted', pathinfo($file)['filename'] ) ); } else { $app->enqueueMessage( sprintf( 'Cannot delete file %s', pathinfo($file)['filename'] ), 'error' ); } } } else { $app->enqueueMessage('No files to delete', 'error'); } return true; } /** * Method override to check if you can add a new record. * * @param array $data An array of input data. * * @return boolean * * @since 1.6 */ protected function allowAdd($data = array()) { // Get user object. $user = JFactory::getUser(); // In the absense of better information, revert to the component permissions. return parent::allowAdd($data); } /** * Method override to check if you can edit an existing record. * * @param array $data An array of input data. * @param string $key The name of the key for the primary key. * * @return boolean * * @since 1.6 */ protected function allowEdit($data = array(), $key = 'id') { // get user object. $user = JFactory::getUser(); // get record id. $recordId = (int) isset($data[$key]) ? $data[$key] : 0; if ($recordId) { // The record has been set. Check the record permissions. $permission = $user->authorise('core.edit', 'com_extensiondistributor.extension.' . (int) $recordId); if (!$permission) { if ($user->authorise('core.edit.own', 'com_extensiondistributor.extension.' . $recordId)) { // Fallback on edit.own. Now test the owner is the user. $ownerId = (int) isset($data['created_by']) ? $data['created_by'] : 0; if (empty($ownerId)) { // Need to do a lookup from the model. $record = $this->getModel()->getItem($recordId); if (empty($record)) { return false; } $ownerId = $record->created_by; } // If the owner matches 'me' then do the test. if ($ownerId == $user->id) { if ($user->authorise('core.edit.own', 'com_extensiondistributor')) { return true; } } } return false; } } // Since there is no permission, revert to the component permissions. return parent::allowEdit($data, $key); } /** * Gets the URL arguments to append to an item redirect. * * @param integer $recordId The primary key id for the item. * @param string $urlVar The name of the URL variable for the id. * * @return string The arguments to append to the redirect URL. * * @since 1.6 */ protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') { // get the referral options (old method use return instead see parent) $ref = $this->input->get('ref', 0, 'string'); $refid = $this->input->get('refid', 0, 'int'); // get redirect info. $append = parent::getRedirectToItemAppend($recordId, $urlVar); // set the referral options if ($refid && $ref) { $append = '&ref=' . (string)$ref . '&refid='. (int)$refid . $append; } elseif ($ref) { $append = '&ref='. (string)$ref . $append; } return $append; } /** * Method to run batch operations. * * @param object $model The model. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 2.5 */ public function batch($model = null) { JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); // Set the model $model = $this->getModel('Extension', '', array()); // Preset the redirect $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view=extensions' . $this->getRedirectToListAppend(), false)); return parent::batch($model); } /** * Method to cancel an edit. * * @param string $key The name of the primary key of the URL variable. * * @return boolean True if access level checks pass, false otherwise. * * @since 12.2 */ public function cancel($key = null) { // get the referral options $this->ref = $this->input->get('ref', 0, 'word'); $this->refid = $this->input->get('refid', 0, 'int'); // Check if there is a return value $return = $this->input->get('return', null, 'base64'); $cancel = parent::cancel($key); if (!is_null($return) && JUri::isInternal(base64_decode($return))) { $redirect = base64_decode($return); // Redirect to the return value. $this->setRedirect( JRoute::_( $redirect, false ) ); } elseif ($this->refid && $this->ref) { $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; // Redirect to the item screen. $this->setRedirect( JRoute::_( 'index.php?option=' . $this->option . $redirect, false ) ); } elseif ($this->ref) { $redirect = '&view='.(string)$this->ref; // Redirect to the list screen. $this->setRedirect( JRoute::_( 'index.php?option=' . $this->option . $redirect, false ) ); } return $cancel; } /** * Method to save a record. * * @param string $key The name of the primary key of the URL variable. * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). * * @return boolean True if successful, false otherwise. * * @since 12.2 */ public function save($key = null, $urlVar = null) { // get the referral options $this->ref = $this->input->get('ref', 0, 'word'); $this->refid = $this->input->get('refid', 0, 'int'); // Check if there is a return value $return = $this->input->get('return', null, 'base64'); $canReturn = (!is_null($return) && JUri::isInternal(base64_decode($return))); if ($this->ref || $this->refid || $canReturn) { // to make sure the item is checkedin on redirect $this->task = 'save'; } $saved = parent::save($key, $urlVar); // This is not needed since parent save already does this // Due to the ref and refid implementation we need to add this if ($canReturn) { $redirect = base64_decode($return); // Redirect to the return value. $this->setRedirect( JRoute::_( $redirect, false ) ); } elseif ($this->refid && $this->ref) { $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; // Redirect to the item screen. $this->setRedirect( JRoute::_( 'index.php?option=' . $this->option . $redirect, false ) ); } elseif ($this->ref) { $redirect = '&view=' . (string)$this->ref; // Redirect to the list screen. $this->setRedirect( JRoute::_( 'index.php?option=' . $this->option . $redirect, false ) ); } return $saved; } /** * Function that allows child controller access to model data * after the data has been saved. * * @param JModel &$model The data model object. * @param array $validData The validated data. * * @return void * * @since 11.1 */ protected function postSaveHook(JModelLegacy $model, $validData = array()) { return; } }