1<?php 2/** 3 * @package Joomla.Administrator 4 * @subpackage com_templates 5 * 6 * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10defined('_JEXEC') or die; 11 12/** 13 * Template model class. 14 * 15 * @since 1.6 16 */ 17class TemplatesModelTemplate extends JModelForm 18{ 19 /** 20 * The information in a template 21 * 22 * @var stdClass 23 * @since 1.6 24 */ 25 protected $template = null; 26 27 /** 28 * The path to the template 29 * 30 * @var stdClass 31 * @since 3.2 32 */ 33 protected $element = null; 34 35 /** 36 * Internal method to get file properties. 37 * 38 * @param string $path The base path. 39 * @param string $name The file name. 40 * 41 * @return object 42 * 43 * @since 1.6 44 */ 45 protected function getFile($path, $name) 46 { 47 $temp = new stdClass; 48 49 if ($template = $this->getTemplate()) 50 { 51 $temp->name = $name; 52 $temp->id = urlencode(base64_encode($path . $name)); 53 54 return $temp; 55 } 56 } 57 58 /** 59 * Method to get a list of all the files to edit in a template. 60 * 61 * @return array A nested array of relevant files. 62 * 63 * @since 1.6 64 */ 65 public function getFiles() 66 { 67 $result = array(); 68 69 if ($template = $this->getTemplate()) 70 { 71 jimport('joomla.filesystem.folder'); 72 $app = JFactory::getApplication(); 73 $client = JApplicationHelper::getClientInfo($template->client_id); 74 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 75 $lang = JFactory::getLanguage(); 76 77 // Load the core and/or local language file(s). 78 $lang->load('tpl_' . $template->element, $client->path, null, false, true) || 79 $lang->load('tpl_' . $template->element, $client->path . '/templates/' . $template->element, null, false, true); 80 $this->element = $path; 81 82 if (!is_writable($path)) 83 { 84 $app->enqueueMessage(JText::_('COM_TEMPLATES_DIRECTORY_NOT_WRITABLE'), 'error'); 85 } 86 87 if (is_dir($path)) 88 { 89 $result = $this->getDirectoryTree($path); 90 } 91 else 92 { 93 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_TEMPLATE_FOLDER_NOT_FOUND'), 'error'); 94 95 return false; 96 } 97 } 98 99 return $result; 100 } 101 102 /** 103 * Get the directory tree. 104 * 105 * @param string $dir The path of the directory to scan 106 * 107 * @return array 108 * 109 * @since 3.2 110 */ 111 public function getDirectoryTree($dir) 112 { 113 $result = array(); 114 115 $dirFiles = scandir($dir); 116 117 foreach ($dirFiles as $key => $value) 118 { 119 if (!in_array($value, array('.', '..'))) 120 { 121 if (is_dir($dir . $value)) 122 { 123 $relativePath = str_replace($this->element, '', $dir . $value); 124 $result['/' . $relativePath] = $this->getDirectoryTree($dir . $value . '/'); 125 } 126 else 127 { 128 $ext = pathinfo($dir . $value, PATHINFO_EXTENSION); 129 $allowedFormat = $this->checkFormat($ext); 130 131 if ($allowedFormat == true) 132 { 133 $relativePath = str_replace($this->element, '', $dir); 134 $info = $this->getFile('/' . $relativePath, $value); 135 $result[] = $info; 136 } 137 } 138 } 139 } 140 141 return $result; 142 } 143 144 /** 145 * Method to auto-populate the model state. 146 * 147 * Note. Calling getState in this method will result in recursion. 148 * 149 * @return void 150 * 151 * @since 1.6 152 */ 153 protected function populateState() 154 { 155 jimport('joomla.filesystem.file'); 156 $app = JFactory::getApplication('administrator'); 157 158 // Load the User state. 159 $pk = $app->input->getInt('id'); 160 $this->setState('extension.id', $pk); 161 162 // Load the parameters. 163 $params = JComponentHelper::getParams('com_templates'); 164 $this->setState('params', $params); 165 } 166 167 /** 168 * Method to get the template information. 169 * 170 * @return mixed Object if successful, false if not and internal error is set. 171 * 172 * @since 1.6 173 */ 174 public function &getTemplate() 175 { 176 if (empty($this->template)) 177 { 178 $pk = $this->getState('extension.id'); 179 $db = $this->getDbo(); 180 $app = JFactory::getApplication(); 181 182 // Get the template information. 183 $query = $db->getQuery(true) 184 ->select('extension_id, client_id, element, name, manifest_cache') 185 ->from('#__extensions') 186 ->where($db->quoteName('extension_id') . ' = ' . (int) $pk) 187 ->where($db->quoteName('type') . ' = ' . $db->quote('template')); 188 $db->setQuery($query); 189 190 try 191 { 192 $result = $db->loadObject(); 193 } 194 catch (RuntimeException $e) 195 { 196 $app->enqueueMessage($e->getMessage(), 'warning'); 197 $this->template = false; 198 199 return false; 200 } 201 202 if (empty($result)) 203 { 204 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_EXTENSION_RECORD_NOT_FOUND'), 'error'); 205 $this->template = false; 206 } 207 else 208 { 209 $this->template = $result; 210 } 211 } 212 213 return $this->template; 214 } 215 216 /** 217 * Method to check if new template name already exists 218 * 219 * @return boolean true if name is not used, false otherwise 220 * 221 * @since 2.5 222 */ 223 public function checkNewName() 224 { 225 $db = $this->getDbo(); 226 $query = $db->getQuery(true) 227 ->select('COUNT(*)') 228 ->from('#__extensions') 229 ->where('name = ' . $db->quote($this->getState('new_name'))); 230 $db->setQuery($query); 231 232 return ($db->loadResult() == 0); 233 } 234 235 /** 236 * Method to check if new template name already exists 237 * 238 * @return string name of current template 239 * 240 * @since 2.5 241 */ 242 public function getFromName() 243 { 244 return $this->getTemplate()->element; 245 } 246 247 /** 248 * Method to check if new template name already exists 249 * 250 * @return boolean true if name is not used, false otherwise 251 * 252 * @since 2.5 253 */ 254 public function copy() 255 { 256 $app = JFactory::getApplication(); 257 258 if ($template = $this->getTemplate()) 259 { 260 jimport('joomla.filesystem.folder'); 261 $client = JApplicationHelper::getClientInfo($template->client_id); 262 $fromPath = JPath::clean($client->path . '/templates/' . $template->element . '/'); 263 264 // Delete new folder if it exists 265 $toPath = $this->getState('to_path'); 266 267 if (JFolder::exists($toPath)) 268 { 269 if (!JFolder::delete($toPath)) 270 { 271 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_COULD_NOT_WRITE'), 'error'); 272 273 return false; 274 } 275 } 276 277 // Copy all files from $fromName template to $newName folder 278 if (!JFolder::copy($fromPath, $toPath) || !$this->fixTemplateName()) 279 { 280 return false; 281 } 282 283 return true; 284 } 285 else 286 { 287 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_INVALID_FROM_NAME'), 'error'); 288 289 return false; 290 } 291 } 292 293 /** 294 * Method to delete tmp folder 295 * 296 * @return boolean true if delete successful, false otherwise 297 * 298 * @since 2.5 299 */ 300 public function cleanup() 301 { 302 // Clear installation messages 303 $app = JFactory::getApplication(); 304 $app->setUserState('com_installer.message', ''); 305 $app->setUserState('com_installer.extension_message', ''); 306 307 // Delete temporary directory 308 return JFolder::delete($this->getState('to_path')); 309 } 310 311 /** 312 * Method to rename the template in the XML files and rename the language files 313 * 314 * @return boolean true if successful, false otherwise 315 * 316 * @since 2.5 317 */ 318 protected function fixTemplateName() 319 { 320 // Rename Language files 321 // Get list of language files 322 $result = true; 323 $files = JFolder::files($this->getState('to_path'), '.ini', true, true); 324 $newName = strtolower($this->getState('new_name')); 325 $template = $this->getTemplate(); 326 $oldName = $template->element; 327 $manifest = json_decode($template->manifest_cache); 328 329 jimport('joomla.filesystem.file'); 330 331 foreach ($files as $file) 332 { 333 $newFile = '/' . str_replace($oldName, $newName, basename($file)); 334 $result = JFile::move($file, dirname($file) . $newFile) && $result; 335 } 336 337 // Edit XML file 338 $xmlFile = $this->getState('to_path') . '/templateDetails.xml'; 339 340 if (JFile::exists($xmlFile)) 341 { 342 $contents = file_get_contents($xmlFile); 343 $pattern[] = '#<name>\s*' . $manifest->name . '\s*</name>#i'; 344 $replace[] = '<name>' . $newName . '</name>'; 345 $pattern[] = '#<language(.*)' . $oldName . '(.*)</language>#'; 346 $replace[] = '<language${1}' . $newName . '${2}</language>'; 347 $contents = preg_replace($pattern, $replace, $contents); 348 $result = JFile::write($xmlFile, $contents) && $result; 349 } 350 351 return $result; 352 } 353 354 /** 355 * Method to get the record form. 356 * 357 * @param array $data Data for the form. 358 * @param boolean $loadData True if the form is to load its own data (default case), false if not. 359 * 360 * @return JForm A JForm object on success, false on failure 361 * 362 * @since 1.6 363 */ 364 public function getForm($data = array(), $loadData = true) 365 { 366 $app = JFactory::getApplication(); 367 368 // Codemirror or Editor None should be enabled 369 $db = $this->getDbo(); 370 $query = $db->getQuery(true) 371 ->select('COUNT(*)') 372 ->from('#__extensions as a') 373 ->where( 374 '(a.name =' . $db->quote('plg_editors_codemirror') . 375 ' AND a.enabled = 1) OR (a.name =' . 376 $db->quote('plg_editors_none') . 377 ' AND a.enabled = 1)' 378 ); 379 $db->setQuery($query); 380 $state = $db->loadResult(); 381 382 if ((int) $state < 1) 383 { 384 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_EDITOR_DISABLED'), 'warning'); 385 } 386 387 // Get the form. 388 $form = $this->loadForm('com_templates.source', 'source', array('control' => 'jform', 'load_data' => $loadData)); 389 390 if (empty($form)) 391 { 392 return false; 393 } 394 395 return $form; 396 } 397 398 /** 399 * Method to get the data that should be injected in the form. 400 * 401 * @return mixed The data for the form. 402 * 403 * @since 1.6 404 */ 405 protected function loadFormData() 406 { 407 $data = $this->getSource(); 408 409 $this->preprocessData('com_templates.source', $data); 410 411 return $data; 412 } 413 414 /** 415 * Method to get a single record. 416 * 417 * @return mixed Object on success, false on failure. 418 * 419 * @since 1.6 420 */ 421 public function &getSource() 422 { 423 $app = JFactory::getApplication(); 424 $item = new stdClass; 425 426 if (!$this->template) 427 { 428 $this->getTemplate(); 429 } 430 431 if ($this->template) 432 { 433 $input = JFactory::getApplication()->input; 434 $fileName = base64_decode($input->get('file')); 435 $client = JApplicationHelper::getClientInfo($this->template->client_id); 436 437 try 438 { 439 $filePath = JPath::check($client->path . '/templates/' . $this->template->element . '/' . $fileName); 440 } 441 catch (Exception $e) 442 { 443 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_SOURCE_FILE_NOT_FOUND'), 'error'); 444 445 return; 446 } 447 448 if (file_exists($filePath)) 449 { 450 $item->extension_id = $this->getState('extension.id'); 451 $item->filename = $fileName; 452 $item->source = file_get_contents($filePath); 453 } 454 else 455 { 456 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_SOURCE_FILE_NOT_FOUND'), 'error'); 457 } 458 } 459 460 return $item; 461 } 462 463 /** 464 * Method to store the source file contents. 465 * 466 * @param array $data The source data to save. 467 * 468 * @return boolean True on success, false otherwise and internal error set. 469 * 470 * @since 1.6 471 */ 472 public function save($data) 473 { 474 jimport('joomla.filesystem.file'); 475 476 // Get the template. 477 $template = $this->getTemplate(); 478 479 if (empty($template)) 480 { 481 return false; 482 } 483 484 $app = JFactory::getApplication(); 485 $fileName = base64_decode($app->input->get('file')); 486 $client = JApplicationHelper::getClientInfo($template->client_id); 487 $filePath = JPath::clean($client->path . '/templates/' . $template->element . '/' . $fileName); 488 489 // Include the extension plugins for the save events. 490 JPluginHelper::importPlugin('extension'); 491 492 $user = get_current_user(); 493 chown($filePath, $user); 494 JPath::setPermissions($filePath, '0644'); 495 496 // Try to make the template file writable. 497 if (!is_writable($filePath)) 498 { 499 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_SOURCE_FILE_NOT_WRITABLE'), 'warning'); 500 $app->enqueueMessage(JText::sprintf('COM_TEMPLATES_FILE_PERMISSIONS', JPath::getPermissions($filePath)), 'warning'); 501 502 if (!JPath::isOwner($filePath)) 503 { 504 $app->enqueueMessage(JText::_('COM_TEMPLATES_CHECK_FILE_OWNERSHIP'), 'warning'); 505 } 506 507 return false; 508 } 509 510 // Make sure EOL is Unix 511 $data['source'] = str_replace(array("\r\n", "\r"), "\n", $data['source']); 512 513 $return = JFile::write($filePath, $data['source']); 514 515 if (!$return) 516 { 517 $app->enqueueMessage(JText::sprintf('COM_TEMPLATES_ERROR_FAILED_TO_SAVE_FILENAME', $fileName), 'error'); 518 519 return false; 520 } 521 522 // Get the extension of the changed file. 523 $explodeArray = explode('.', $fileName); 524 $ext = end($explodeArray); 525 526 if ($ext == 'less') 527 { 528 $app->enqueueMessage(JText::sprintf('COM_TEMPLATES_COMPILE_LESS', $fileName)); 529 } 530 531 return true; 532 } 533 534 /** 535 * Get overrides folder. 536 * 537 * @param string $name The name of override. 538 * @param string $path Location of override. 539 * 540 * @return object containing override name and path. 541 * 542 * @since 3.2 543 */ 544 public function getOverridesFolder($name,$path) 545 { 546 $folder = new stdClass; 547 $folder->name = $name; 548 $folder->path = base64_encode($path . $name); 549 550 return $folder; 551 } 552 553 /** 554 * Get a list of overrides. 555 * 556 * @return array containing overrides. 557 * 558 * @since 3.2 559 */ 560 public function getOverridesList() 561 { 562 if ($template = $this->getTemplate()) 563 { 564 $client = JApplicationHelper::getClientInfo($template->client_id); 565 $componentPath = JPath::clean($client->path . '/components/'); 566 $modulePath = JPath::clean($client->path . '/modules/'); 567 $pluginPath = JPath::clean(JPATH_ROOT . '/plugins/'); 568 $layoutPath = JPath::clean(JPATH_ROOT . '/layouts/'); 569 $components = JFolder::folders($componentPath); 570 571 foreach ($components as $component) 572 { 573 if (file_exists($componentPath . '/' . $component . '/views/')) 574 { 575 $viewPath = JPath::clean($componentPath . '/' . $component . '/views/'); 576 } 577 elseif (file_exists($componentPath . '/' . $component . '/view/')) 578 { 579 $viewPath = JPath::clean($componentPath . '/' . $component . '/view/'); 580 } 581 else 582 { 583 $viewPath = ''; 584 } 585 586 if ($viewPath) 587 { 588 $views = JFolder::folders($viewPath); 589 590 foreach ($views as $view) 591 { 592 // Only show the view has layout inside it 593 if (file_exists($viewPath . $view . '/tmpl')) 594 { 595 $result['components'][$component][] = $this->getOverridesFolder($view, $viewPath); 596 } 597 } 598 } 599 } 600 601 foreach (JFolder::folders($pluginPath) as $pluginGroup) 602 { 603 foreach (JFolder::folders($pluginPath . '/' . $pluginGroup) as $plugin) 604 { 605 if (file_exists($pluginPath . '/' . $pluginGroup . '/' . $plugin . '/tmpl/')) 606 { 607 $pluginLayoutPath = JPath::clean($pluginPath . '/' . $pluginGroup . '/'); 608 $result['plugins'][$pluginGroup][] = $this->getOverridesFolder($plugin, $pluginLayoutPath); 609 } 610 } 611 } 612 613 $modules = JFolder::folders($modulePath); 614 615 foreach ($modules as $module) 616 { 617 $result['modules'][] = $this->getOverridesFolder($module, $modulePath); 618 } 619 620 $layoutFolders = JFolder::folders($layoutPath); 621 622 foreach ($layoutFolders as $layoutFolder) 623 { 624 $layoutFolderPath = JPath::clean($layoutPath . '/' . $layoutFolder . '/'); 625 $layouts = JFolder::folders($layoutFolderPath); 626 627 foreach ($layouts as $layout) 628 { 629 $result['layouts'][$layoutFolder][] = $this->getOverridesFolder($layout, $layoutFolderPath); 630 } 631 } 632 633 // Check for layouts in component folders 634 foreach ($components as $component) 635 { 636 if (file_exists($componentPath . '/' . $component . '/layouts/')) 637 { 638 $componentLayoutPath = JPath::clean($componentPath . '/' . $component . '/layouts/'); 639 640 if ($componentLayoutPath) 641 { 642 $layouts = JFolder::folders($componentLayoutPath); 643 644 foreach ($layouts as $layout) 645 { 646 $result['layouts'][$component][] = $this->getOverridesFolder($layout, $componentLayoutPath); 647 } 648 } 649 } 650 } 651 } 652 653 if (!empty($result)) 654 { 655 return $result; 656 } 657 } 658 659 /** 660 * Create overrides. 661 * 662 * @param string $override The override location. 663 * 664 * @return boolean true if override creation is successful, false otherwise 665 * 666 * @since 3.2 667 */ 668 public function createOverride($override) 669 { 670 jimport('joomla.filesystem.folder'); 671 672 if ($template = $this->getTemplate()) 673 { 674 $app = JFactory::getApplication(); 675 $explodeArray = explode(DIRECTORY_SEPARATOR, $override); 676 $name = end($explodeArray); 677 $client = JApplicationHelper::getClientInfo($template->client_id); 678 679 if (stristr($name, 'mod_') != false) 680 { 681 $htmlPath = JPath::clean($client->path . '/templates/' . $template->element . '/html/' . $name); 682 } 683 elseif (stristr($override, 'com_') != false) 684 { 685 $size = count($explodeArray); 686 687 $url = JPath::clean($explodeArray[$size - 3] . '/' . $explodeArray[$size - 1]); 688 689 if ($explodeArray[$size - 2] == 'layouts') 690 { 691 $htmlPath = JPath::clean($client->path . '/templates/' . $template->element . '/html/layouts/' . $url); 692 } 693 else 694 { 695 $htmlPath = JPath::clean($client->path . '/templates/' . $template->element . '/html/' . $url); 696 } 697 } 698 elseif (stripos($override, JPath::clean(JPATH_ROOT . '/plugins/')) === 0) 699 { 700 $size = count($explodeArray); 701 $layoutPath = JPath::clean('plg_' . $explodeArray[$size - 2] . '_' . $explodeArray[$size - 1]); 702 $htmlPath = JPath::clean($client->path . '/templates/' . $template->element . '/html/' . $layoutPath); 703 } 704 else 705 { 706 $layoutPath = implode('/', array_slice($explodeArray, -2)); 707 $htmlPath = JPath::clean($client->path . '/templates/' . $template->element . '/html/layouts/' . $layoutPath); 708 } 709 710 // Check Html folder, create if not exist 711 if (!JFolder::exists($htmlPath)) 712 { 713 if (!JFolder::create($htmlPath)) 714 { 715 $app->enqueueMessage(JText::_('COM_TEMPLATES_FOLDER_ERROR'), 'error'); 716 717 return false; 718 } 719 } 720 721 if (stristr($name, 'mod_') != false) 722 { 723 $return = $this->createTemplateOverride(JPath::clean($override . '/tmpl'), $htmlPath); 724 } 725 elseif (stristr($override, 'com_') != false && stristr($override, 'layouts') == false) 726 { 727 $return = $this->createTemplateOverride(JPath::clean($override . '/tmpl'), $htmlPath); 728 } 729 elseif (stripos($override, JPath::clean(JPATH_ROOT . '/plugins/')) === 0) 730 { 731 $return = $this->createTemplateOverride(JPath::clean($override . '/tmpl'), $htmlPath); 732 } 733 else 734 { 735 $return = $this->createTemplateOverride($override, $htmlPath); 736 } 737 738 if ($return) 739 { 740 $app->enqueueMessage(JText::_('COM_TEMPLATES_OVERRIDE_CREATED') . str_replace(JPATH_ROOT, '', $htmlPath)); 741 742 return true; 743 } 744 else 745 { 746 $app->enqueueMessage(JText::_('COM_TEMPLATES_OVERRIDE_FAILED'), 'error'); 747 748 return false; 749 } 750 } 751 } 752 753 /** 754 * Create override folder & file 755 * 756 * @param string $overridePath The override location 757 * @param string $htmlPath The html location 758 * 759 * @return boolean True on success. False otherwise. 760 */ 761 public function createTemplateOverride($overridePath, $htmlPath) 762 { 763 $return = false; 764 765 if (empty($overridePath) || empty($htmlPath)) 766 { 767 return $return; 768 } 769 770 // Get list of template folders 771 $folders = JFolder::folders($overridePath, null, true, true); 772 773 if (!empty($folders)) 774 { 775 foreach ($folders as $folder) 776 { 777 $htmlFolder = $htmlPath . str_replace($overridePath, '', $folder); 778 779 if (!JFolder::exists($htmlFolder)) 780 { 781 JFolder::create($htmlFolder); 782 } 783 } 784 } 785 786 // Get list of template files (Only get *.php file for template file) 787 $files = JFolder::files($overridePath, '.php', true, true); 788 789 if (empty($files)) 790 { 791 return true; 792 } 793 794 foreach ($files as $file) 795 { 796 $overrideFilePath = str_replace($overridePath, '', $file); 797 $htmlFilePath = $htmlPath . $overrideFilePath; 798 799 if (JFile::exists($htmlFilePath)) 800 { 801 // Generate new unique file name base on current time 802 $today = JFactory::getDate(); 803 $htmlFilePath = JFile::stripExt($htmlFilePath) . '-' . $today->format('Ymd-His') . '.' . JFile::getExt($htmlFilePath); 804 } 805 806 $return = JFile::copy($file, $htmlFilePath, '', true); 807 } 808 809 return $return; 810 } 811 812 /** 813 * Compile less using the less compiler under /build. 814 * 815 * @param string $input The relative location of the less file. 816 * 817 * @return boolean true if compilation is successful, false otherwise 818 * 819 * @since 3.2 820 */ 821 public function compileLess($input) 822 { 823 if ($template = $this->getTemplate()) 824 { 825 $app = JFactory::getApplication(); 826 $client = JApplicationHelper::getClientInfo($template->client_id); 827 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 828 $inFile = urldecode(base64_decode($input)); 829 $explodeArray = explode('/', $inFile); 830 $fileName = end($explodeArray); 831 $outFile = current(explode('.', $fileName)); 832 833 $less = new JLess; 834 $less->setFormatter(new JLessFormatterJoomla); 835 836 try 837 { 838 $less->compileFile($path . $inFile, $path . 'css/' . $outFile . '.css'); 839 840 return true; 841 } 842 catch (Exception $e) 843 { 844 $app->enqueueMessage($e->getMessage(), 'error'); 845 } 846 } 847 } 848 849 /** 850 * Delete a particular file. 851 * 852 * @param string $file The relative location of the file. 853 * 854 * @return boolean True if file deletion is successful, false otherwise 855 * 856 * @since 3.2 857 */ 858 public function deleteFile($file) 859 { 860 if ($template = $this->getTemplate()) 861 { 862 $app = JFactory::getApplication(); 863 $client = JApplicationHelper::getClientInfo($template->client_id); 864 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 865 $filePath = $path . urldecode(base64_decode($file)); 866 867 $return = JFile::delete($filePath); 868 869 if (!$return) 870 { 871 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_DELETE_FAIL'), 'error'); 872 873 return false; 874 } 875 876 return true; 877 } 878 } 879 880 /** 881 * Create new file. 882 * 883 * @param string $name The name of file. 884 * @param string $type The extension of the file. 885 * @param string $location Location for the new file. 886 * 887 * @return boolean true if file created successfully, false otherwise 888 * 889 * @since 3.2 890 */ 891 public function createFile($name, $type, $location) 892 { 893 if ($template = $this->getTemplate()) 894 { 895 $app = JFactory::getApplication(); 896 $client = JApplicationHelper::getClientInfo($template->client_id); 897 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 898 899 if (file_exists(JPath::clean($path . '/' . $location . '/' . $name . '.' . $type))) 900 { 901 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_EXISTS'), 'error'); 902 903 return false; 904 } 905 906 if (!fopen(JPath::clean($path . '/' . $location . '/' . $name . '.' . $type), 'x')) 907 { 908 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_CREATE_ERROR'), 'error'); 909 910 return false; 911 } 912 913 // Check if the format is allowed and will be showed in the backend 914 $check = $this->checkFormat($type); 915 916 // Add a message if we are not allowed to show this file in the backend. 917 if (!$check) 918 { 919 $app->enqueueMessage(JText::sprintf('COM_TEMPLATES_WARNING_FORMAT_WILL_NOT_BE_VISIBLE', $type), 'warning'); 920 } 921 922 return true; 923 } 924 } 925 926 /** 927 * Upload new file. 928 * 929 * @param string $file The name of the file. 930 * @param string $location Location for the new file. 931 * 932 * @return boolean True if file uploaded successfully, false otherwise 933 * 934 * @since 3.2 935 */ 936 public function uploadFile($file, $location) 937 { 938 jimport('joomla.filesystem.folder'); 939 940 if ($template = $this->getTemplate()) 941 { 942 $app = JFactory::getApplication(); 943 $client = JApplicationHelper::getClientInfo($template->client_id); 944 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 945 $fileName = JFile::makeSafe($file['name']); 946 947 $err = null; 948 JLoader::register('TemplateHelper', JPATH_ADMINISTRATOR . '/components/com_templates/helpers/template.php'); 949 950 if (!TemplateHelper::canUpload($file, $err)) 951 { 952 // Can't upload the file 953 return false; 954 } 955 956 if (file_exists(JPath::clean($path . '/' . $location . '/' . $file['name']))) 957 { 958 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_EXISTS'), 'error'); 959 960 return false; 961 } 962 963 if (!JFile::upload($file['tmp_name'], JPath::clean($path . '/' . $location . '/' . $fileName))) 964 { 965 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_UPLOAD_ERROR'), 'error'); 966 967 return false; 968 } 969 970 $url = JPath::clean($location . '/' . $fileName); 971 972 return $url; 973 } 974 } 975 976 /** 977 * Create new folder. 978 * 979 * @param string $name The name of the new folder. 980 * @param string $location Location for the new folder. 981 * 982 * @return boolean True if override folder is created successfully, false otherwise 983 * 984 * @since 3.2 985 */ 986 public function createFolder($name, $location) 987 { 988 jimport('joomla.filesystem.folder'); 989 990 if ($template = $this->getTemplate()) 991 { 992 $app = JFactory::getApplication(); 993 $client = JApplicationHelper::getClientInfo($template->client_id); 994 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 995 996 if (file_exists(JPath::clean($path . '/' . $location . '/' . $name . '/'))) 997 { 998 $app->enqueueMessage(JText::_('COM_TEMPLATES_FOLDER_EXISTS'), 'error'); 999 1000 return false; 1001 } 1002 1003 if (!JFolder::create(JPath::clean($path . '/' . $location . '/' . $name))) 1004 { 1005 $app->enqueueMessage(JText::_('COM_TEMPLATES_FOLDER_CREATE_ERROR'), 'error'); 1006 1007 return false; 1008 } 1009 1010 return true; 1011 } 1012 } 1013 1014 /** 1015 * Delete a folder. 1016 * 1017 * @param string $location The name and location of the folder. 1018 * 1019 * @return boolean True if override folder is deleted successfully, false otherwise 1020 * 1021 * @since 3.2 1022 */ 1023 public function deleteFolder($location) 1024 { 1025 jimport('joomla.filesystem.folder'); 1026 1027 if ($template = $this->getTemplate()) 1028 { 1029 $app = JFactory::getApplication(); 1030 $client = JApplicationHelper::getClientInfo($template->client_id); 1031 $path = JPath::clean($client->path . '/templates/' . $template->element . '/' . $location); 1032 1033 if (!file_exists($path)) 1034 { 1035 $app->enqueueMessage(JText::_('COM_TEMPLATES_FOLDER_NOT_EXISTS'), 'error'); 1036 1037 return false; 1038 } 1039 1040 $return = JFolder::delete($path); 1041 1042 if (!$return) 1043 { 1044 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_DELETE_ERROR'), 'error'); 1045 1046 return false; 1047 } 1048 1049 return true; 1050 } 1051 } 1052 1053 /** 1054 * Rename a file. 1055 * 1056 * @param string $file The name and location of the old file 1057 * @param string $name The new name of the file. 1058 * 1059 * @return string Encoded string containing the new file location. 1060 * 1061 * @since 3.2 1062 */ 1063 public function renameFile($file, $name) 1064 { 1065 if ($template = $this->getTemplate()) 1066 { 1067 $app = JFactory::getApplication(); 1068 $client = JApplicationHelper::getClientInfo($template->client_id); 1069 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 1070 $fileName = base64_decode($file); 1071 $explodeArray = explode('.', $fileName); 1072 $type = end($explodeArray); 1073 $explodeArray = explode('/', $fileName); 1074 $newName = str_replace(end($explodeArray), $name . '.' . $type, $fileName); 1075 1076 if (file_exists($path . $newName)) 1077 { 1078 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_EXISTS'), 'error'); 1079 1080 return false; 1081 } 1082 1083 if (!rename($path . $fileName, $path . $newName)) 1084 { 1085 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_RENAME_ERROR'), 'error'); 1086 1087 return false; 1088 } 1089 1090 return base64_encode($newName); 1091 } 1092 } 1093 1094 /** 1095 * Get an image address, height and width. 1096 * 1097 * @return array an associative array containing image address, height and width. 1098 * 1099 * @since 3.2 1100 */ 1101 public function getImage() 1102 { 1103 if ($template = $this->getTemplate()) 1104 { 1105 $app = JFactory::getApplication(); 1106 $client = JApplicationHelper::getClientInfo($template->client_id); 1107 $fileName = base64_decode($app->input->get('file')); 1108 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 1109 1110 if (stristr($client->path, 'administrator') == false) 1111 { 1112 $folder = '/templates/'; 1113 } 1114 else 1115 { 1116 $folder = '/administrator/templates/'; 1117 } 1118 1119 $uri = JUri::root(true) . $folder . $template->element; 1120 1121 if (file_exists(JPath::clean($path . $fileName))) 1122 { 1123 $JImage = new JImage(JPath::clean($path . $fileName)); 1124 $image['address'] = $uri . $fileName; 1125 $image['path'] = $fileName; 1126 $image['height'] = $JImage->getHeight(); 1127 $image['width'] = $JImage->getWidth(); 1128 } 1129 1130 else 1131 { 1132 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_IMAGE_FILE_NOT_FOUND'), 'error'); 1133 1134 return false; 1135 } 1136 1137 return $image; 1138 } 1139 } 1140 1141 /** 1142 * Crop an image. 1143 * 1144 * @param string $file The name and location of the file 1145 * @param string $w width. 1146 * @param string $h height. 1147 * @param string $x x-coordinate. 1148 * @param string $y y-coordinate. 1149 * 1150 * @return boolean true if image cropped successfully, false otherwise. 1151 * 1152 * @since 3.2 1153 */ 1154 public function cropImage($file, $w, $h, $x, $y) 1155 { 1156 if ($template = $this->getTemplate()) 1157 { 1158 $app = JFactory::getApplication(); 1159 $client = JApplicationHelper::getClientInfo($template->client_id); 1160 $relPath = base64_decode($file); 1161 $path = JPath::clean($client->path . '/templates/' . $template->element . '/' . $relPath); 1162 1163 try 1164 { 1165 $image = new \JImage($path); 1166 $properties = $image->getImageFileProperties($path); 1167 1168 switch ($properties->mime) 1169 { 1170 case 'image/png': 1171 $imageType = \IMAGETYPE_PNG; 1172 break; 1173 case 'image/gif': 1174 $imageType = \IMAGETYPE_GIF; 1175 break; 1176 default: 1177 $imageType = \IMAGETYPE_JPEG; 1178 } 1179 1180 $image->crop($w, $h, $x, $y, false); 1181 $image->toFile($path, $imageType); 1182 1183 return true; 1184 } 1185 catch (Exception $e) 1186 { 1187 $app->enqueueMessage($e->getMessage(), 'error'); 1188 } 1189 } 1190 } 1191 1192 /** 1193 * Resize an image. 1194 * 1195 * @param string $file The name and location of the file 1196 * @param string $width The new width of the image. 1197 * @param string $height The new height of the image. 1198 * 1199 * @return boolean true if image resize successful, false otherwise. 1200 * 1201 * @since 3.2 1202 */ 1203 public function resizeImage($file, $width, $height) 1204 { 1205 if ($template = $this->getTemplate()) 1206 { 1207 $app = JFactory::getApplication(); 1208 $client = JApplicationHelper::getClientInfo($template->client_id); 1209 $relPath = base64_decode($file); 1210 $path = JPath::clean($client->path . '/templates/' . $template->element . '/' . $relPath); 1211 1212 try 1213 { 1214 $image = new \JImage($path); 1215 $properties = $image->getImageFileProperties($path); 1216 1217 switch ($properties->mime) 1218 { 1219 case 'image/png': 1220 $imageType = \IMAGETYPE_PNG; 1221 break; 1222 case 'image/gif': 1223 $imageType = \IMAGETYPE_GIF; 1224 break; 1225 default: 1226 $imageType = \IMAGETYPE_JPEG; 1227 } 1228 1229 $image->resize($width, $height, false, \JImage::SCALE_FILL); 1230 $image->toFile($path, $imageType); 1231 1232 return true; 1233 } 1234 catch (Exception $e) 1235 { 1236 $app->enqueueMessage($e->getMessage(), 'error'); 1237 } 1238 } 1239 } 1240 1241 /** 1242 * Template preview. 1243 * 1244 * @return object object containing the id of the template. 1245 * 1246 * @since 3.2 1247 */ 1248 public function getPreview() 1249 { 1250 $app = JFactory::getApplication(); 1251 $db = $this->getDbo(); 1252 $query = $db->getQuery(true); 1253 1254 $query->select('id, client_id'); 1255 $query->from('#__template_styles'); 1256 $query->where($db->quoteName('template') . ' = ' . $db->quote($this->template->element)); 1257 1258 $db->setQuery($query); 1259 1260 try 1261 { 1262 $result = $db->loadObject(); 1263 } 1264 catch (RuntimeException $e) 1265 { 1266 $app->enqueueMessage($e->getMessage(), 'warning'); 1267 } 1268 1269 if (empty($result)) 1270 { 1271 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_EXTENSION_RECORD_NOT_FOUND'), 'warning'); 1272 } 1273 else 1274 { 1275 return $result; 1276 } 1277 } 1278 1279 /** 1280 * Rename a file. 1281 * 1282 * @return mixed array on success, false on failure 1283 * 1284 * @since 3.2 1285 */ 1286 public function getFont() 1287 { 1288 if ($template = $this->getTemplate()) 1289 { 1290 $app = JFactory::getApplication(); 1291 $client = JApplicationHelper::getClientInfo($template->client_id); 1292 $relPath = base64_decode($app->input->get('file')); 1293 $explodeArray = explode('/', $relPath); 1294 $fileName = end($explodeArray); 1295 $path = JPath::clean($client->path . '/templates/' . $template->element . '/' . $relPath); 1296 1297 if (stristr($client->path, 'administrator') == false) 1298 { 1299 $folder = '/templates/'; 1300 } 1301 else 1302 { 1303 $folder = '/administrator/templates/'; 1304 } 1305 1306 $uri = JUri::root(true) . $folder . $template->element; 1307 1308 if (file_exists(JPath::clean($path))) 1309 { 1310 $font['address'] = $uri . $relPath; 1311 1312 $font['rel_path'] = $relPath; 1313 1314 $font['name'] = $fileName; 1315 } 1316 1317 else 1318 { 1319 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_FONT_FILE_NOT_FOUND'), 'error'); 1320 1321 return false; 1322 } 1323 1324 return $font; 1325 } 1326 } 1327 1328 /** 1329 * Copy a file. 1330 * 1331 * @param string $newName The name of the copied file 1332 * @param string $location The final location where the file is to be copied 1333 * @param string $file The name and location of the file 1334 * 1335 * @return boolean true if image resize successful, false otherwise. 1336 * 1337 * @since 3.2 1338 */ 1339 public function copyFile($newName, $location, $file) 1340 { 1341 if ($template = $this->getTemplate()) 1342 { 1343 $app = JFactory::getApplication(); 1344 $client = JApplicationHelper::getClientInfo($template->client_id); 1345 $relPath = base64_decode($file); 1346 $explodeArray = explode('.', $relPath); 1347 $ext = end($explodeArray); 1348 $path = JPath::clean($client->path . '/templates/' . $template->element . '/'); 1349 $newPath = JPath::clean($path . '/' . $location . '/' . $newName . '.' . $ext); 1350 1351 if (file_exists($newPath)) 1352 { 1353 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_EXISTS'), 'error'); 1354 1355 return false; 1356 } 1357 1358 if (JFile::copy($path . $relPath, $newPath)) 1359 { 1360 $app->enqueueMessage(JText::sprintf('COM_TEMPLATES_FILE_COPY_SUCCESS', $newName . '.' . $ext)); 1361 1362 return true; 1363 } 1364 else 1365 { 1366 return false; 1367 } 1368 } 1369 } 1370 1371 /** 1372 * Get the compressed files. 1373 * 1374 * @return array if file exists, false otherwise 1375 * 1376 * @since 3.2 1377 */ 1378 public function getArchive() 1379 { 1380 if ($template = $this->getTemplate()) 1381 { 1382 $app = JFactory::getApplication(); 1383 $client = JApplicationHelper::getClientInfo($template->client_id); 1384 $relPath = base64_decode($app->input->get('file')); 1385 $path = JPath::clean($client->path . '/templates/' . $template->element . '/' . $relPath); 1386 1387 if (file_exists(JPath::clean($path))) 1388 { 1389 $files = array(); 1390 $zip = new ZipArchive; 1391 1392 if ($zip->open($path) === true) 1393 { 1394 for ($i = 0; $i < $zip->numFiles; $i++) 1395 { 1396 $entry = $zip->getNameIndex($i); 1397 $files[] = $entry; 1398 } 1399 } 1400 else 1401 { 1402 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_ARCHIVE_OPEN_FAIL'), 'error'); 1403 1404 return false; 1405 } 1406 } 1407 else 1408 { 1409 $app->enqueueMessage(JText::_('COM_TEMPLATES_ERROR_FONT_FILE_NOT_FOUND'), 'error'); 1410 1411 return false; 1412 } 1413 1414 return $files; 1415 } 1416 } 1417 1418 /** 1419 * Extract contents of an archive file. 1420 * 1421 * @param string $file The name and location of the file 1422 * 1423 * @return boolean true if image extraction is successful, false otherwise. 1424 * 1425 * @since 3.2 1426 */ 1427 public function extractArchive($file) 1428 { 1429 if ($template = $this->getTemplate()) 1430 { 1431 $app = JFactory::getApplication(); 1432 $client = JApplicationHelper::getClientInfo($template->client_id); 1433 $relPath = base64_decode($file); 1434 $explodeArray = explode('/', $relPath); 1435 $fileName = end($explodeArray); 1436 $folderPath = stristr($relPath, $fileName, true); 1437 $path = JPath::clean($client->path . '/templates/' . $template->element . '/' . $folderPath . '/'); 1438 1439 if (file_exists(JPath::clean($path . '/' . $fileName))) 1440 { 1441 $zip = new ZipArchive; 1442 1443 if ($zip->open(JPath::clean($path . '/' . $fileName)) === true) 1444 { 1445 for ($i = 0; $i < $zip->numFiles; $i++) 1446 { 1447 $entry = $zip->getNameIndex($i); 1448 1449 if (file_exists(JPath::clean($path . '/' . $entry))) 1450 { 1451 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_ARCHIVE_EXISTS'), 'error'); 1452 1453 return false; 1454 } 1455 } 1456 1457 $zip->extractTo($path); 1458 1459 return true; 1460 } 1461 else 1462 { 1463 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_ARCHIVE_OPEN_FAIL'), 'error'); 1464 1465 return false; 1466 } 1467 } 1468 else 1469 { 1470 $app->enqueueMessage(JText::_('COM_TEMPLATES_FILE_ARCHIVE_NOT_FOUND'), 'error'); 1471 1472 return false; 1473 } 1474 } 1475 } 1476 1477 /** 1478 * Check if the extension is allowed and will be shown in the template manager 1479 * 1480 * @param string $ext The extension to check if it is allowed 1481 * 1482 * @return boolean true if the extension is allowed false otherwise 1483 * 1484 * @since 3.6.0 1485 */ 1486 protected function checkFormat($ext) 1487 { 1488 if (!isset($this->allowedFormats)) 1489 { 1490 $params = JComponentHelper::getParams('com_templates'); 1491 $imageTypes = explode(',', $params->get('image_formats')); 1492 $sourceTypes = explode(',', $params->get('source_formats')); 1493 $fontTypes = explode(',', $params->get('font_formats')); 1494 $archiveTypes = explode(',', $params->get('compressed_formats')); 1495 1496 $this->allowedFormats = array_merge($imageTypes, $sourceTypes, $fontTypes, $archiveTypes); 1497 } 1498 1499 return in_array($ext, $this->allowedFormats); 1500 } 1501} 1502