Skip to content

Commit

Permalink
Fix multiple issues with theme assets (#1048)
Browse files Browse the repository at this point in the history
Fixed issue #13606 : Unable to reset manually single asset
Dev: hash function use for whole path, then global var set here
Dev: generatePath is used path by path

Fixed issue #13497 : Editor screen can be loaded without css after save
Dev: moving global reset to only this template
Dev: less time to generate assets

Fixed issue #13596: template assets is not resetted when update config
Dev: need to do it if user add a file in TemplateConfiguration_files_*
Dev: do it only for global theme (?)

Dev: move injectglobalsettings to LSYii_Application
Dev: maybe create a now function to set all config ?

Dev: Fix installmer and move all set configs part to one function

Dev: Create a DB for asset version number and use it for Template

Dev: fix upgrade , remove dev test lien :/

Fixed issue : reset asset when upload or delete a template file
Dev: rename Template functions to be more clear (we don't delete really the asset directory)
  • Loading branch information
Shnoulle authored and LouisGac committed Jun 4, 2018
1 parent d8f91ff commit c3d2521
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 69 deletions.
1 change: 0 additions & 1 deletion application/controllers/admin/themeoptions.php
Expand Up @@ -80,7 +80,6 @@ public function update($id)
{
if (Permission::model()->hasGlobalPermission('templates', 'update')) {
$model = $this->loadModel($id);

if (isset($_POST['TemplateConfiguration'])) {
$model->attributes = $_POST['TemplateConfiguration'];
if ($model->save()) {
Expand Down
7 changes: 5 additions & 2 deletions application/controllers/admin/themes.php
Expand Up @@ -346,6 +346,7 @@ public function uploadfile()
$uploadresult = gT("An error occurred uploading your file. This may be caused by incorrect permissions for the application /tmp folder.");
} else {
$uploadresult = sprintf(gT("File %s uploaded"), $filename);
Template::model()->findByPk($templatename)->resetAssetVersion(); // Upload a files, asset need to be resetted (maybe)
$status = 'success';
}
}
Expand Down Expand Up @@ -461,6 +462,7 @@ public function templatefiledelete()

if (@unlink($the_full_file_path)) {
Yii::app()->user->setFlash('success', sprintf(gT("The file %s was deleted."), htmlspecialchars($sFileToDelete)));
Template::model()->findByPk($sTemplateName)->resetAssetVersion(); // Delete a files, asset need to be resetted (maybe)
} else {
Yii::app()->user->setFlash('error', sprintf(gT("File %s couldn't be deleted. Please check the permissions on the /upload/themes folder"), htmlspecialchars($sFileToDelete)));
}
Expand Down Expand Up @@ -583,8 +585,8 @@ public function delete($templatename)
if (Template::checkIfTemplateExists($templatename) && !Template::isStandardTemplate($templatename)) {

if (!Template::hasInheritance($templatename)) {

if (rmdirr(Yii::app()->getConfig('userthemerootdir')."/".$templatename)) {
Template::model()->findByPk($templatename)->deleteAssetVersion();
$surveys = Survey::model()->findAllByAttributes(array('template' => $templatename));

// The default template could be the same as the one we're trying to remove
Expand Down Expand Up @@ -698,7 +700,8 @@ public function templatesavechanges()

// If the file is an asset file, we refresh asset number
if (in_array($relativePathEditfile, $cssfiles) || in_array($relativePathEditfile, $jsfiles)){
SettingGlobal::increaseCustomAssetsversionnumber();
//SettingGlobal::increaseCustomAssetsversionnumber();
Template::model()->findByPk($sTemplateName)->resetAssetVersion();
}

fclose($handle);
Expand Down
63 changes: 47 additions & 16 deletions application/core/LSYii_Application.php
Expand Up @@ -75,29 +75,19 @@ public function __construct($aApplicationConfig = null)
parent::__construct($aApplicationConfig);

/* Because we have app now : we have to call again the config (usage of Yii::app() for publicurl) */
$coreConfig = require(__DIR__.'/../config/config-defaults.php');
$emailConfig = require(__DIR__.'/../config/email.php');
$versionConfig = require(__DIR__.'/../config/version.php');
$updaterVersionConfig = require(__DIR__.'/../config/updater_version.php');
$lsConfig = array_merge($coreConfig, $emailConfig, $versionConfig, $updaterVersionConfig);
if (file_exists(__DIR__.'/../config/config.php')) {
$userConfigs = require(__DIR__.'/../config/config.php');
if (is_array($userConfigs['config'])) {
$lsConfig = array_merge($lsConfig, $userConfigs['config']);
}
}
$this->setConfigs();

/* Update asset manager path and url only if not directly set in aApplicationConfig (from config.php),
* must do after reloading to have valid publicurl (the tempurl) */
if (!isset($aApplicationConfig['components']['assetManager']['baseUrl'])) {
App()->getAssetManager()->setBaseUrl($lsConfig['tempurl'].'/assets');
App()->getAssetManager()->setBaseUrl($this->config['tempurl'].'/assets');
}
if (!isset($aApplicationConfig['components']['assetManager']['basePath'])) {
App()->getAssetManager()->setBasePath($lsConfig['tempdir'].'/assets');
App()->getAssetManager()->setBasePath($this->config['tempdir'].'/assets');
}

$this->config = array_merge($this->config, $lsConfig);
}

/* @inheritdoc */
public function init()
{
parent::init();
Expand All @@ -108,16 +98,57 @@ public function init()
ClassFactory::registerClass('Response_', 'Response');
}

/* @inheritdoc */
public function initLanguage()
{
// Set language to use.
if ($this->request->getParam('lang') !== null) {
$this->setLanguage($this->request->getParam('lang'));
} elseif (isset(App()->session['_lang'])) {
// See: http://www.yiiframework.com/wiki/26/setting-and-maintaining-the-language-in-application-i18n/
// See: http://www.yiiframework.com/wiki/26/setting-and-maintaining-the-language-in-application-i18n/
$this->setLanguage(App()->session['_lang']);
}
}

/**
* Set the LimeSUrvey config array according to files and DB
* @return void
*/
public function setConfigs() {
/* Default config */
$coreConfig = require(__DIR__.'/../config/config-defaults.php');
$emailConfig = require(__DIR__.'/../config/email.php');
$versionConfig = require(__DIR__.'/../config/version.php');
$updaterVersionConfig = require(__DIR__.'/../config/updater_version.php');
$this->config = array_merge($this->config,$coreConfig, $emailConfig, $versionConfig, $updaterVersionConfig);
if(!file_exists(__DIR__.'/../config/config.php')) {
/* Set up not done : then no other part to update */
return;
}
/* User file config */
$userConfigs = require(__DIR__.'/../config/config.php');
if (is_array($userConfigs['config'])) {
$this->config = array_merge($this->config, $userConfigs['config']);
}
/* Database config */
try {
$settingsTableExist = Yii::app()->db->schema->getTable('{{settings_global}}');
if (is_object($settingsTableExist)) {
$dbConfig = CHtml::listData(SettingGlobal::model()->findAll(), 'stg_name', 'stg_value');
$this->config = array_merge($this->config, $dbConfig);
}
} catch (Exception $exception) {
/* Even if database can exist : don't throw exception, */
/* @todo : find when settings_global was created with stg_name and stg_value, maybe can Throw Exception ? */
Yii::log("Table settings_global not found");// Log it as LEVEL_INFO , application category
}
/* Add some specific config using exiting other configs */
$this->setConfig('globalAssetsVersion', /* Or create a new var ? */
$this->getConfig('assetsversionnumber',0).
$this->getConfig('versionnumber',0).
$this->getConfig('dbversionnumber',0).
$this->getConfig('customassetversionnumber',1)
);
}
/**
* Loads a helper
Expand Down
48 changes: 13 additions & 35 deletions application/core/LSYii_AssetManager.php
@@ -1,10 +1,9 @@
<?php

/**
* LimeSurvey
* Copyright (C) 2007-2011 The LimeSurvey Project Team / Carsten Schmitz
* Copyright (C) 2007-2018 The LimeSurvey Project Team / Carsten Schmitz
* All rights reserved.
* License: GNU/GPL License v2 or later, see LICENSE.php
* License: GNU/GPL License v3 or later, see LICENSE.php
* LimeSurvey is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
Expand All @@ -14,44 +13,23 @@

class LSYii_AssetManager extends CAssetManager
{
/* @inheritdoc */
protected function hash($path)
{
return sprintf('%x',crc32($path.Yii::app()->getConfig('globalAssetsVersion')));
}

/**
* Generates path segments relative to basePath.
*
* This method is used instead of the original, so the hash is taken
* from LS version number instead of folder/file last modified time.
* Using file/folder causes a lot of problems due to FTP and other file
* transfers not updating the time stamp, forcing LS to use touch()
* in a lot of places instead. touch() can now be removed - the assets
* will be updated every time a version number is changed.
*
* @param string $file for which public path will be created.
* @param bool $hashByName whether the published directory should be named as the hashed basename.
* @return string path segments without basePath.
* @since 1.1.13
* @inheritdoc
* With db asset version used
*/
protected function generatePath($file, $hashByName = false)
protected function generatePath($file,$hashByName=false)
{
$assetsVersionNumber = Yii::app()->getConfig('assetsversionnumber');
$versionNumber = Yii::app()->getConfig('versionnumber');
$dbVersion = Yii::app()->getConfig('dbversionnumber');
$iCustomassetversionnumber = (function_exists('getGlobalSetting') ) ? getGlobalSetting('customassetversionnumber'):1; // When called from installer, function getGlobalSetting() is not available

if (empty($assetsVersionNumber)
|| empty($versionNumber)
|| empty($dbVersion)) {
throw new Exception(
'Could not create asset manager path hash: One of these configs are empty: assetsversionnumber/versionnumber/dbversionnumber.'
);
}

$lsVersion = $assetsVersionNumber.$versionNumber.$dbVersion.$iCustomassetversionnumber;

if (is_file($file)) {
$pathForHashing = $hashByName ? dirname($file) : dirname($file).$lsVersion;
$pathForHashing=$hashByName ? dirname($file) : dirname($file).".".filemtime($file).".".AssetVersion::getAssetVersion($file);
} else {
$pathForHashing = $hashByName ? $file : $file.$lsVersion;
$pathForHashing=$hashByName ? $file : $file.".".filemtime($file).".".AssetVersion::getAssetVersion($file);
}

return $this->hash($pathForHashing);
}
}
14 changes: 0 additions & 14 deletions application/helpers/globalsettings_helper.php
Expand Up @@ -15,20 +15,6 @@
*/
//Ensure script is not run directly, avoid path disclosure
//if (!isset($homedir) || isset($_REQUEST['$homedir'])) {die("Cannot run this script directly");}
injectglobalsettings();


function injectglobalsettings()
{
$settings = SettingGlobal::model()->findAll();

//if ($dbvaluearray!==false)
if (count($settings) > 0) {
foreach ($settings as $setting) {
Yii::app()->setConfig($setting->getAttribute('stg_name'), $setting->getAttribute('stg_value'));
}
}
}

/**
* Returns a global setting
Expand Down
15 changes: 15 additions & 0 deletions application/helpers/update/updatedb_helper.php
Expand Up @@ -2202,13 +2202,28 @@ function db_upgrade_all($iOldDBVersion, $bSilent = false)
$oDB->createCommand()->update('{{settings_global}}', ['stg_value'=>348], "stg_name='DBVersion'");
$oTransaction->commit();
}

if ($iOldDBVersion < 349) {
$oTransaction = $oDB->beginTransaction();
dropColumn('{{users}}','one_time_pw');
addColumn('{{users}}','one_time_pw','text');
$oDB->createCommand()->update('{{settings_global}}', ['stg_value'=>349], "stg_name='DBVersion'");
$oTransaction->commit();
}

/**
* Adding asset version to allow to reset asset without write inside
*/
if ($iOldDBVersion < 350) {
$oTransaction = $oDB->beginTransaction();
$oDB->createCommand()->createTable('{{asset_version}}',array(
'id' => 'pk',
'path' => 'text NOT NULL',
'version' => 'integer NOT NULL',
));
$oDB->createCommand()->update('{{settings_global}}', ['stg_value'=>350], "stg_name='DBVersion'");
$oTransaction->commit();
}

} catch (Exception $e) {
Yii::app()->setConfig('Updating', false);
Expand Down
108 changes: 108 additions & 0 deletions application/models/AssetVersion.php
@@ -0,0 +1,108 @@
<?php if (!defined('BASEPATH')) {exit('No direct script access allowed');}
/*
* LimeSurvey
* Copyright (C) 2018 The LimeSurvey Project Team / Carsten Schmitz
* All rights reserved.
* License: GNU/GPL License v3 or later, see LICENSE.php
* LimeSurvey is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*
*/

/**
* Class AssetVersion
*
* @property integer $id pk
* @property string $path
* @property integer $version number
*/
class AssetVersion extends LSActiveRecord
{
/**
* @inheritdoc
*/
public static function model($class = __CLASS__)
{
return parent::model($class);
}

/**
* @inheritdoc
*/
public function init()
{
$this->path = "";
$this->version = 0;
}
/** @inheritdoc */
public function tableName()
{
return '{{asset_version}}';
}

/** @inheritdoc */
public function rules()
{
return array(
array('path', 'required'),
array('path', 'unique'),
array('version', 'required'),
array('version', 'numerical', 'integerOnly'=>true),
);
}

/**
* get current assetVersion
* @param string $path
* @return integer
*/
public static function getAssetVersion($path)
{
if(Yii::app()->getConfig('DBVersion') < 349) {
return 0;
}
$oAssetVersion = self::model()->find('path = :path',array(":path"=>$path));
if(!$oAssetVersion) {
return 0;
}
return $oAssetVersion->version;
}

/**
* increment (and create if needed) asset version number
* @param string $path
* @return integer (current version)
*/
public static function incrementAssetVersion($path)
{
if(Yii::app()->getConfig('DBVersion') < 349) {
return 0;
}
/* This increment case insensitivity , (extend_vanilla at same time than Extend_Vanilla) no real issue (update 2 assets in one) , but … */
$oAssetVersion = self::model()->find('path = :path',array(":path"=>$path));
if(!$oAssetVersion) {
$oAssetVersion = new self;
$oAssetVersion->path = $path;
$oAssetVersion->version = 0;
}
$oAssetVersion->version++;
$oAssetVersion->save(); // Not need to test : can not break rules. DB error can happen ?
return $oAssetVersion->version;
}

/**
* delete assets version related to path
* @param string $path
* @return integer (0|1)
*/
public static function deleteAssetVersion($path)
{
if(Yii::app()->getConfig('DBVersion') < 349) {
return 0;
}
return self::model()->deleteAll('path = :path',array(":path"=>$path));
}
}

0 comments on commit c3d2521

Please sign in to comment.