Skip to content

Commit

Permalink
New feature: Install/Uninstall templates uploaded via FTP
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisGac committed Oct 13, 2017
1 parent 812cefa commit e9b27f1
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 52 deletions.
18 changes: 17 additions & 1 deletion application/controllers/admin/templateoptions.php
Expand Up @@ -174,7 +174,10 @@ public function index()

$aData = array();

$model = new TemplateConfiguration('search');
//$model = new TemplateConfiguration('search');
$model = new TemplateConfiguration();

//$aFtpTemplates = ;

$aData['model'] = $model;
$this->_renderWrappedTemplate('templateoptions', 'index', $aData);
Expand Down Expand Up @@ -213,6 +216,19 @@ public function loadModel($id)
return $model;
}


public function importManifest($templatename)
{
TemplateManifest::importManifest($templatename);
$this->getController()->redirect(array("admin/templateoptions"));
}

public function uninstall($templatename)
{
TemplateConfiguration::uninstall($templatename);
$this->getController()->redirect(array("admin/templateoptions"));
}

/**
* Performs the AJAX validation.
* @param TemplateOptions $model the model to be validated
Expand Down
32 changes: 32 additions & 0 deletions application/models/Template.php
Expand Up @@ -37,6 +37,8 @@
*/
class Template extends LSActiveRecord
{
/** @var array $aTemplatesInUploadDir cache for the method getUploadTemplates */
public static $aTemplatesInUploadDir = null;

/** @var Template - The instance of template object */
private static $instance;
Expand Down Expand Up @@ -215,6 +217,8 @@ public static function getTemplateConfiguration($sTemplateName=null, $iSurveyId=
// If no row found, or if the template folder for this configuration row doesn't exist we load the XML config (which will load the default XML)
if ( $bForceXML || !is_a($oTemplateConfigurationModel, 'TemplateConfiguration') || ! $oTemplateConfigurationModel->checkTemplate()){
$oTemplateConfigurationModel = new TemplateManifest;
$oTemplateConfigurationModel->setBasics($sTemplateName, $iSurveyId);

}

//$oTemplateConfigurationModel->prepareTemplateRendering($sTemplateName, $iSurveyId);
Expand Down Expand Up @@ -464,6 +468,34 @@ public static function getStandardTemplateList()
// return self::$standardTemplates;
}




public static function getUploadTemplates()
{

if(empty(self::$aTemplatesInUploadDir)){

$sUserTemplateRootDir = Yii::app()->getConfig("usertemplaterootdir");
$aTemplateList = array();

if ($sUserTemplateRootDir && $handle = opendir($sUserTemplateRootDir)){

while (false !== ($sFileName = readdir($handle))){

if (!is_file("$sUserTemplateRootDir/$sFileName") && $sFileName != "." && $sFileName != ".." && $sFileName!=".svn" && (file_exists("{$sUserTemplateRootDir}/{$sFileName}/config.xml") )){
$aTemplateList[$sFileName] = $sUserTemplateRootDir.DIRECTORY_SEPARATOR.$sFileName;
}
}
closedir($handle);
}
ksort($aTemplateList);
self::$aTemplatesInUploadDir = $aTemplateList;
}

return self::$aTemplatesInUploadDir;
}

/**
* Retrieves a list of models based on the current search/filter conditions.
*
Expand Down
54 changes: 54 additions & 0 deletions application/models/TemplateConfig.php
Expand Up @@ -784,6 +784,60 @@ public function __toString()
return $s;
}


public static function uninstall($templatename )
{
if (Permission::model()->hasGlobalPermission('templates','delete'))
{
$oTemplate = Template::model()->findByAttributes(array('name' => $templatename));
if( $oTemplate->delete()){
$oTemplateConfig = TemplateConfiguration::model()->findByAttributes(array('template_name' => $templatename));
return TemplateConfiguration::model()->deleteAll('template_name=:templateName', array(':templateName' => $templatename) );
}
}
return false;
}

/**
* Create a new entry in {{templates}} and {{template_configuration}} table using the template manifest
* @param string $sTemplateName the name of the template to import
* @return mixed true on success | exception
* @throws Exception
*/
public static function importManifest($sTemplateName, $aDatas )
{
$oNewTemplate = new Template;
$oNewTemplate->name = $sTemplateName;
$oNewTemplate->folder = $sTemplateName;
$oNewTemplate->title = $sTemplateName; // For now, when created via template editor => name == folder == title
$oNewTemplate->creation_date = date("Y-m-d H:i:s");
$oNewTemplate->author = Yii::app()->user->name;
$oNewTemplate->author_email = ''; // privacy
$oNewTemplate->author_url = ''; // privacy
$oNewTemplate->api_version = $aDatas['api_version'];
$oNewTemplate->view_folder = $aDatas['view_folder'];
$oNewTemplate->files_folder = $aDatas['files_folder'];
//$oNewTemplate->description TODO: a more complex modal whith email, author, url, licence, desc, etc
$oNewTemplate->owner_id = Yii::app()->user->id;
$oNewTemplate->extends = $aDatas['extends'];

if ($oNewTemplate->save()){
$oNewTemplateConfiguration = new TemplateConfiguration;
$oNewTemplateConfiguration->template_name = $sTemplateName;
$oNewTemplateConfiguration->template_name = $sTemplateName;
$oNewTemplateConfiguration->options = json_encode($aDatas['aOptions']);


if ($oNewTemplateConfiguration->save()){
return true;
}else{
throw new Exception($oNewTemplateConfiguration->getErrors());
}
}else{
throw new Exception($oNewTemplate->getErrors());
}
}

// TODO: try to refactore most of those methods in TemplateConfiguration and TemplateManifest so we can define their body here.
// It will consist in adding private methods to get the values of variables... See what has been done for createTemplatePackage
// Then, the lonely differences between TemplateManifest and TemplateConfiguration should be how to retreive and format the data
Expand Down
129 changes: 83 additions & 46 deletions application/models/TemplateConfiguration.php
Expand Up @@ -54,6 +54,8 @@ class TemplateConfiguration extends TemplateConfig

// Caches

public $allDbTemplateFolders = null;

/** @var string $sPreviewImgTag the template preview image tag for the template list*/
public $sPreviewImgTag;

Expand Down Expand Up @@ -308,42 +310,23 @@ public function getPreview()
* @return mixed true on success | exception
* @throws Exception
*/
public static function importManifest($sTemplateName)
public static function importManifest($sTemplateName, $aDatas=array() )
{
$oEditedTemplate = Template::model()->getTemplateConfiguration($sTemplateName, null,null, false);
$oEditedTemplate->prepareTemplateRendering($sTemplateName);

$oEditTemplateDb = Template::model()->findByPk($oEditedTemplate->oMotherTemplate->sTemplateName);
$oNewTemplate = new Template;
$oNewTemplate->name = $oEditedTemplate->sTemplateName;
$oNewTemplate->folder = $oEditedTemplate->sTemplateName;
$oNewTemplate->title = $oEditedTemplate->sTemplateName; // For now, when created via template editor => name == folder == title
$oNewTemplate->creation_date = date("Y-m-d H:i:s");
$oNewTemplate->author = Yii::app()->user->name;
$oNewTemplate->author_email = ''; // privacy
$oNewTemplate->author_url = ''; // privacy
$oNewTemplate->api_version = $oEditTemplateDb->api_version;
$oNewTemplate->view_folder = $oEditTemplateDb->view_folder;
$oNewTemplate->files_folder = $oEditTemplateDb->files_folder;
//$oNewTemplate->description TODO: a more complex modal whith email, author, url, licence, desc, etc
$oNewTemplate->owner_id = Yii::app()->user->id;
$oNewTemplate->extends = $oEditedTemplate->oMotherTemplate->sTemplateName;

if ($oNewTemplate->save()){
$oNewTemplateConfiguration = new TemplateConfiguration;
$oNewTemplateConfiguration->template_name = $oEditedTemplate->sTemplateName;
$oNewTemplateConfiguration->template_name = $oEditedTemplate->sTemplateName;
$oNewTemplateConfiguration->options = json_encode($oEditedTemplate->oOptions);


if ($oNewTemplateConfiguration->save()){
return true;
}else{
throw new Exception($oNewTemplateConfiguration->getErrors());
}
}else{
throw new Exception($oNewTemplate->getErrors());
if ( !empty($aDatas['extends']) ){

$oEditTemplateDb = Template::model()->findByPk($aDatas['extends']);

$aDatas['api_version'] = $oEditTemplateDb->api_version;
$aDatas['view_folder'] = $oEditTemplateDb->view_folder;
$aDatas['author_email'] = $oEditTemplateDb->author_email;
$aDatas['author_url'] = $oEditTemplateDb->author_url;
$aDatas['copyright'] = $oEditTemplateDb->copyright;
$aDatas['version'] = $oEditTemplateDb->version;
$aDatas['license'] = $oEditTemplateDb->license;
$aDatas['files_folder'] = $oEditTemplateDb->files_folder;
}

return parent::importManifest($sTemplateName, $aDatas );
}

public function setToInherit(){
Expand Down Expand Up @@ -382,15 +365,20 @@ public function checkTemplate()
public function prepareTemplateRendering($sTemplateName='', $iSurveyId='', $bUseMagicInherit=true)
{
$this->bUseMagicInherit = $bUseMagicInherit;
$this->setBasics($sTemplateName, $iSurveyId);
$this->setMotherTemplates(); // Recursive mother templates configuration
$this->setThisTemplate(); // Set the main config values of this template
$this->createTemplatePackage($this); // Create an asset package ready to be loaded
return $this;
}

public function setBasics($sTemplateName='', $iSurveyId='')
{
$this->sTemplateName = $this->template->name;
$this->setIsStandard(); // Check if it is a CORE template
$this->path = ($this->isStandard)
? Yii::app()->getConfig("standardtemplaterootdir").DIRECTORY_SEPARATOR.$this->template->folder
: Yii::app()->getConfig("usertemplaterootdir").DIRECTORY_SEPARATOR.$this->template->folder;
$this->setMotherTemplates(); // Recursive mother templates configuration
$this->setThisTemplate(); // Set the main config values of this template
$this->createTemplatePackage($this); // Create an asset package ready to be loaded
return $this;
}

/**
Expand Down Expand Up @@ -430,11 +418,12 @@ public function getTypeIcon()

public function getButtons()
{
$sEditorUrl = Yii::app()->getController()->createUrl('admin/templates/sa/view', array("templatename"=>$this->template->name));
$sOptionUrl = Yii::app()->getController()->createUrl('admin/templateoptions/sa/update', array("id"=>$this->id));
$sEditorUrl = Yii::app()->getController()->createUrl('admin/templates/sa/view', array("templatename"=>$this->template_name));
$sOptionUrl = Yii::app()->getController()->createUrl('admin/templateoptions/sa/update', array("id"=>$this->id));
$sUninstallUrl = Yii::app()->getController()->createUrl('admin/templateoptions/sa/uninstall/', array("templatename"=>$this->template_name));

$sEditorLink = "<a
id='template_editor_link'
id='template_editor_link_".$this->template_name."'
href='".$sEditorUrl."'
class='btn btn-default'>
<span class='icon-templates'></span>
Expand All @@ -447,17 +436,30 @@ class='btn btn-default'>

if ($this->hasOptionPage){
$OptionLink .= "<a
id='template_options_link'
id='template_options_link_".$this->template_name."'
href='".$sOptionUrl."'
class='btn btn-default'>
<span class='fa fa-tachometer'></span>
".gT('Template options')."
</a>";
}

$sUninstallLink = "<a
id='remove_fromdb_link_".$this->template_name."'
href='".$sUninstallUrl."'
class='btn btn-default btn-danger'>
<span class='icon-trash'></span>
".gT('Uninstall')."
</a>";


$sButtons = $sEditorLink.'<br><br>'.$OptionLink;

if (!$this->isStandard){
$sButtons .= '<br><br>'.$sUninstallLink ;
}

return $sEditorLink.'<br><br>'.$OptionLink;
return $sButtons;
}

public function getHasOptionPage()
Expand Down Expand Up @@ -518,7 +520,7 @@ protected function getFilesToLoad($oTemplate, $sType)
}
}
}


return $this->aFilesToLoad[$sType];
}
Expand Down Expand Up @@ -681,7 +683,7 @@ protected function getFrameworkAssetsToReplace( $sType, $bInlcudeRemove = false)
$this->aFrameworkAssetsToReplace[$sType] = array_merge($this->aFrameworkAssetsToReplace, (array) $aFieldValue['remove'] );
}
}


return $this->aFrameworkAssetsToReplace[$sType];
}
Expand All @@ -705,7 +707,7 @@ protected function getFrameworkAssetsReplacement( $sType )
$aReplace = $aAsset[1];
$this->aReplacements[$sType][] = $aReplace;
}


return $this->aReplacements[$sType];
}
Expand Down Expand Up @@ -748,6 +750,41 @@ public function getParentConfiguration(){
return $this->oParentTemplate;
}


public function getTemplatesWithNoDb()
{
$aTemplatesInUpload = Template::getUploadTemplates();
$aTemplatesInDb = $this->getAllDbTemplateFolders();
$aTemplatesWithoutDB = array();

foreach ($aTemplatesInUpload as $sName => $sPath) {
if (! in_array($sName, $aTemplatesInDb) ){
$aTemplatesWithoutDB[$sName] = Template::getTemplateConfiguration($sName, null, null, true); // Get the manifest
}
}

return $aTemplatesWithoutDB;
}

public function getAllDbTemplateFolders()
{
if (empty($this->allDbTemplateFolders)){

$oCriteria = new CDbCriteria;
$oCriteria->select = 'folder';
$oAllDbTemplateFolders = Template::model()->findAll($oCriteria);

$aAllDbTemplateFolders = array();
foreach ($oAllDbTemplateFolders as $oAllDbTemplateFolders){
$aAllDbTemplateFolders[] = $oAllDbTemplateFolders->folder;
}

$this->allDbTemplateFolders = array_unique($aAllDbTemplateFolders);
}

return $this->allDbTemplateFolders;
}

/**
* Proxy for the AR method to manage the inheritance
* If one of the field that can be inherited is set to "inherit", then it will return the value of its parent
Expand Down

8 comments on commit e9b27f1

@tammoterhark
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I cannot read the code: does this change mean that via FTP uploaded templates are imported automagically?

And what when they are deleted or renamed?

@maziminke
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we now have to install such templates? Previously simply uploading it worked fine.
Users may miss that additional step after FTP upload. What happens in such cases?

@LouisGac
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maziminke :
Now, templates have database entries for configuration at global / survey group / survey level.

@tammoterhark :
Templates are not imported "automagically": after you uploaded a template via FTP, you must press the install button to create the entry in the DB. The idea in template list is to have different arrays:
installed and working templates / working templates but not installed / not working templates (wrong XML, etc) / outdated templates (2.x templates)

Adding an autoinstall of templates in ftp directory can still be done.

@tammoterhark
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any disadvantage on having many (all available) templates installed? Or are there other reasons for not installing templates that are available in the templates directory? Performance? User availability?

@tammoterhark
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting:
autoinstall templates: ON (default)?

@LouisGac
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good idea

@tammoterhark
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And maybe show a list of templates that cannot be installed with a reason:

  • missing config.xml
  • wrong specifications in config.xml
  • using pstpl templates (so it must be for older versions)
  • unknown error
  • etc.

@tammoterhark
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can leave that for 3.1 if it would postpone the release of 3.0 .... ;-)

Please sign in to comment.