vanilla-application-yaga/controllers/class.yagacontroller.php

395 lines
11 KiB
PHP
Raw Normal View History

2014-02-10 15:18:28 -06:00
<?php if(!defined('APPLICATION')) exit();
/* Copyright 2013 Zachary Doll */
/**
* Manage the yaga application including configuration and import/export
*
* @since 1.0
* @package Yaga
*/
class YagaController extends DashboardController {
/**
* @var array These objects will be created on instantiation and available via
* $this->ObjectName
*/
public $Uses = array('Form');
/**
* Make this look like a dashboard page and add the resources
*
* @since 1.0
* @access public
*/
public function Initialize() {
parent::Initialize();
$this->Application = 'Yaga';
Gdn_Theme::Section('Dashboard');
if($this->Menu) {
$this->Menu->HighlightRoute('/yaga');
}
2014-02-06 15:08:29 -06:00
$this->AddSideMenu('yaga/settings');
2014-02-07 14:05:13 -06:00
$this->AddCssFile('yaga.css');
}
2014-02-07 14:05:13 -06:00
/**
* Redirect to settings by default
*/
public function Index() {
$this->Settings();
}
/**
* This handles all the core settings for the gamification application.
*/
public function Settings() {
$this->Permission('Garden.Settings.Manage');
$this->Title(T('Yaga.Settings'));
// Get list of actions from the model and pass to the view
$ConfigModule = new ConfigurationModule($this);
$ConfigModule->Initialize(array(
2014-02-07 14:05:13 -06:00
'Yaga.Reactions.Enabled' => array(
'LabelCode' => 'Use Reactions',
'Control' => 'Checkbox'
),
'Yaga.Badges.Enabled' => array(
'LabelCode' => 'Use Badges',
'Control' => 'Checkbox'
),
'Yaga.Ranks.Enabled' => array(
'LabelCode' => 'Use Ranks',
'Control' => 'Checkbox'
),
'Yaga.LeaderBoard.Enabled' => array(
'LabelCode' => 'Show leaderboard on activity page',
'Control' => 'Checkbox'
),
'Yaga.LeaderBoard.Limit' => array(
'LabelCode' => 'Maximum number of leaders to show',
'Control' => 'Textbox',
'Options' => array(
'Size' => 45,
'class' => 'SmallInput'
)
)
));
$this->ConfigurationModule = $ConfigModule;
2014-02-10 15:18:28 -06:00
$this->Render();
}
2014-02-10 15:18:28 -06:00
/**
* Import a Yaga transport file
*/
public function Import() {
2014-02-06 15:08:29 -06:00
$this->Title(T('Yaga.Import'));
2014-02-08 16:12:59 -06:00
$this->SetData('TransportType', 'Import');
2014-02-10 16:28:50 -06:00
if(!class_exists('ZipArchive')) {
$this->Form->AddError('You do not seem to have the minimum requirements to import a Yaga configuration automatically. Please reference manual_transport.md for more information.');
}
2014-02-10 15:18:28 -06:00
if($this->Form->IsPostBack() == TRUE) {
2014-02-10 14:55:18 -06:00
// Handle the file upload
$Upload = new Gdn_Upload();
$TmpZip = $Upload->ValidateUpload('FileUpload', FALSE);
$ZipFile = FALSE;
if($TmpZip) {
// Generate the target name
$TargetFile = $Upload->GenerateTargetName(PATH_UPLOADS, 'zip');
$BaseName = pathinfo($TargetFile, PATHINFO_BASENAME);
// Save the uploaded zip
$Parts = $Upload->SaveAs($TmpZip, $BaseName);
$ZipFile = PATH_UPLOADS . DS . $Parts['SaveName'];
2014-02-10 16:28:50 -06:00
$this->SetData('TransportPath', $ZipFile);
2014-02-10 14:55:18 -06:00
}
2014-02-10 16:28:50 -06:00
$Include = $this->_FindIncludes();
2014-02-10 14:55:18 -06:00
if(count($Include)) {
2014-02-10 16:28:50 -06:00
$Info = $this->_ExtractZip($ZipFile);
$this->_ImportData($Info, $Include);
Gdn_FileSystem::RemoveFolder(PATH_UPLOADS . DS . 'import' . DS . 'yaga');
2014-02-10 14:55:18 -06:00
}
else {
$this->Form->AddError('You must select at least one item to import.');
}
}
2014-02-10 16:28:50 -06:00
if($this->Form->ErrorCount() == 0 && $this->Form->IsPostBack()) {
$this->Render('transport-success');
}
else {
$this->Render();
}
}
2014-02-07 14:05:13 -06:00
2014-02-10 15:18:28 -06:00
/**
* Create a Yaga transport file
*/
public function Export() {
2014-02-06 15:08:29 -06:00
$this->Title(T('Yaga.Export'));
2014-02-08 16:12:59 -06:00
$this->SetData('TransportType', 'Export');
2014-02-07 14:05:13 -06:00
2014-02-10 16:28:50 -06:00
if(!class_exists('ZipArchive')) {
$this->Form->AddError('You do not seem to have the minimum requirements to export your Yaga configuration automatically. Please reference manual_transport.md for more information.');
}
2014-02-10 14:55:18 -06:00
2014-02-10 16:28:50 -06:00
if($this->Form->IsPostBack()) {
$Include = $this->_FindIncludes();
2014-02-08 16:12:59 -06:00
if(count($Include)) {
2014-02-10 16:28:50 -06:00
$Filename = $this->_ExportData($Include);
$this->SetData('TransportPath', $Filename);
2014-02-08 16:12:59 -06:00
}
else {
$this->Form->AddError('You must select at least one item to export.');
}
2014-02-10 16:28:50 -06:00
}
2014-02-10 14:55:18 -06:00
2014-02-10 16:28:50 -06:00
if($this->Form->ErrorCount() == 0 && $this->Form->IsPostBack()) {
$this->Render('transport-success');
}
else {
$this->Render();
2014-02-08 16:12:59 -06:00
}
2014-02-07 16:03:50 -06:00
}
2014-02-10 14:55:18 -06:00
2014-02-10 16:28:50 -06:00
protected function _FindIncludes() {
$FormValues = $this->Form->FormValues();
$Sections = $FormValues['Checkboxes'];
// Figure out which boxes were checked
$Include = array();
foreach($Sections as $Section) {
$Include[$Section] = $FormValues[$Section];
}
return $Include;
}
2014-02-10 15:18:28 -06:00
/**
* Creates a transport file for easily transferring Yaga configurations across
* installs
2014-02-10 16:28:50 -06:00
*
2014-02-10 15:18:28 -06:00
* @param array An array containing the config areas to transfer
* @param string Where to save the transport file
* @return mixed False on failure, the path to the transport file on success
*/
2014-02-10 16:28:50 -06:00
protected function _ExportData($Include = array(), $Path = NULL) {
2014-02-07 16:03:50 -06:00
$StartTime = microtime(TRUE);
$Info = new stdClass();
$Info->Version = C('Yaga.Version', '?.?');
$Info->StartDate = date('Y-m-d H:i:s');
2014-02-10 14:55:18 -06:00
2014-02-07 16:03:50 -06:00
if(is_null($Path)) {
2014-02-10 15:18:28 -06:00
$Path = PATH_UPLOADS . DS . 'export' . date('Y-m-d-His') . '.yaga.zip';
2014-02-07 16:03:50 -06:00
}
$FH = new ZipArchive();
$Images = array();
$Hashes = array();
2014-02-10 14:55:18 -06:00
if($FH->open($Path, ZipArchive::CREATE) !== TRUE) {
$this->Form->AddError('Unable to create archive: ' . $FH->getStatusString());
2014-02-07 16:03:50 -06:00
return FALSE;
}
2014-02-10 14:55:18 -06:00
// Add actions
2014-02-10 16:28:50 -06:00
if($Include['Action']) {
2014-02-10 15:18:28 -06:00
$Info->Action = 'actions.yaga';
$Actions = Yaga::ActionModel()->Get('Sort', 'asc');
2014-02-10 16:28:50 -06:00
$this->SetData('ActionCount', count($Actions));
$ActionData = serialize($Actions);
$FH->addFromString('actions.yaga', $ActionData);
2014-02-10 14:55:18 -06:00
$Hashes[] = md5($ActionData);
}
2014-02-10 14:55:18 -06:00
// Add ranks and associated image
2014-02-10 16:28:50 -06:00
if($Include['Rank']) {
2014-02-10 15:18:28 -06:00
$Info->Rank = 'ranks.yaga';
$Ranks = Yaga::RankModel()->Get('Level', 'asc');
2014-02-10 16:28:50 -06:00
$this->SetData('RankCount', count($Ranks));
$RankData = serialize($Ranks);
$FH->addFromString('ranks.yaga', $RankData);
array_push($Images, C('Yaga.Ranks.Photo'), NULL);
2014-02-10 14:55:18 -06:00
$Hashes[] = md5($RankData);
}
2014-02-10 14:55:18 -06:00
// Add badges and associated images
2014-02-10 16:28:50 -06:00
if($Include['Badge']) {
2014-02-10 15:18:28 -06:00
$Info->Badge = 'badges.yaga';
$Badges = Yaga::BadgeModel()->Get();
2014-02-10 16:28:50 -06:00
$this->SetData('BadgeCount', count($Badges));
$BadgeData = serialize($Badges);
$FH->addFromString('badges.yaga', $BadgeData);
2014-02-10 14:55:18 -06:00
$Hashes[] = md5($BadgeData);
foreach($Badges as $Badge) {
array_push($Images, $Badge->Photo);
}
2014-02-07 16:03:50 -06:00
}
2014-02-10 14:55:18 -06:00
// Add in any images
2014-02-08 16:12:59 -06:00
$FilteredImages = array_filter($Images);
2014-02-10 16:28:50 -06:00
$ImageCount = count($FilteredImages);
$this->SetData('ImageCount', $ImageCount);
if($ImageCount > 0) {
2014-02-08 16:12:59 -06:00
$FH->addEmptyDir('images');
}
2014-02-10 14:55:18 -06:00
2014-02-08 16:12:59 -06:00
foreach($FilteredImages as $Image) {
2014-02-10 14:55:18 -06:00
if($FH->addFile(PATH_UPLOADS . DS . $Image, 'images' . DS . $Image) !== TRUE) {
2014-02-08 16:12:59 -06:00
$this->Form->AddError('Unable to add file: ' . $FH->getStatusString());
return FALSE;
2014-02-07 16:03:50 -06:00
}
2014-02-10 14:55:18 -06:00
$Hashes[] = md5_file(PATH_UPLOADS . DS . $Image);
2014-02-07 16:03:50 -06:00
}
2014-02-10 14:55:18 -06:00
// Save all the hashes
sort($Hashes);
$Info->MD5 = md5(implode(',', $Hashes));
$Info->EndDate = date('Y-m-d H:i:s');
2014-02-10 14:55:18 -06:00
2014-02-07 14:05:13 -06:00
$EndTime = microtime(TRUE);
$TotalTime = $EndTime - $StartTime;
$m = floor($TotalTime / 60);
$s = $TotalTime - ($m * 60);
$Info->ElapsedTime = sprintf('%02d:%02.2f', $m, $s);
2014-02-10 14:55:18 -06:00
$FH->setArchiveComment(serialize($Info));
2014-02-07 16:03:50 -06:00
if($FH->close()) {
2014-02-08 16:12:59 -06:00
return $Path;
2014-02-07 14:05:13 -06:00
}
else {
2014-02-07 16:03:50 -06:00
$this->Form->AddError('Unable to save archive: ' . $FH->getStatusString());
return FALSE;
2014-02-07 14:05:13 -06:00
}
}
2014-02-10 14:55:18 -06:00
2014-02-10 15:18:28 -06:00
/**
* Extract the transport file and validate
2014-02-10 16:28:50 -06:00
*
2014-02-10 15:18:28 -06:00
* @param string The transport file path
* @return boolean Whether or not the transport file was extracted successfully
*/
2014-02-10 14:55:18 -06:00
protected function _ExtractZip($Filename) {
if(!file_exists($Filename)) {
return FALSE;
// File does not exist
}
$ZipFile = new ZipArchive();
$Result = $ZipFile->open($Filename);
if($Result !== TRUE) {
return FALSE;
// Unable to open file
}
// Get the metadata from the comment
$Comment = $ZipFile->comment;
$MetaData = unserialize($Comment);
2014-02-10 16:28:50 -06:00
2014-02-10 14:55:18 -06:00
$Result = $ZipFile->extractTo(PATH_UPLOADS . DS . 'import' . DS . 'yaga');
if($Result !== TRUE) {
return FALSE;
// Unable to extract file
}
$ZipFile->close();
// Validate checksum
if($this->_ValidateChecksum($MetaData) === TRUE) {
return $MetaData;
}
else {
return FALSE;
// Invalid checksum
}
}
2014-02-10 16:28:50 -06:00
/**
* Dumps the db tables, inserts data, and copies files on over
* @param stdClass The info object read in from the archive
* @param array Which tables should be overwritten
*/
protected function _ImportData($Info, $Include) {
foreach($Include as $Key => $Value) {
if($Value) {
$Data = unserialize(file_get_contents(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $Info->$Key));
Gdn::SQL()->EmptyTable($Key);
$ModelName = $Key . 'Model';
$Model = Yaga::$ModelName();
foreach($Data as $Datum) {
$Model->Insert((array)$Datum);
}
$this->SetData($Key . 'Count', $Model->GetCount());
}
}
if(Gdn_FileSystem::Copy(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . 'images' . DS, PATH_UPLOADS . DS) === FALSE) {
$this->Form->AddError('Unable to copy image files.');
}
}
2014-02-10 15:18:28 -06:00
/**
* Inspects the Yaga transport files and calculates a checksum
2014-02-10 16:28:50 -06:00
*
2014-02-10 15:18:28 -06:00
* @param stdClass The metadata object read in from the transport file
* @return boolean Whether or not the checksum is valid
*/
2014-02-10 14:55:18 -06:00
protected function _ValidateChecksum($MetaData) {
$Hashes = array();
// Hash the data files
if(property_exists($MetaData, 'Actions')) {
$Hashes[] = md5_file(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $MetaData->Actions);
}
2014-02-10 16:28:50 -06:00
2014-02-10 14:55:18 -06:00
if(property_exists($MetaData, 'Badges')) {
$Hashes[] = md5_file(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $MetaData->Badges);
}
2014-02-10 16:28:50 -06:00
2014-02-10 14:55:18 -06:00
if(property_exists($MetaData, 'Ranks')) {
$Hashes[] = md5_file(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $MetaData->Ranks);
}
2014-02-10 16:28:50 -06:00
2014-02-10 14:55:18 -06:00
// Hash the image files
2014-02-10 15:18:28 -06:00
$Files = $this->_GetFiles(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . 'images');
2014-02-10 16:28:50 -06:00
$this->SetData('ImageCount', count($Files));
2014-02-10 14:55:18 -06:00
foreach($Files as $File) {
$Hashes[] = md5_file($File);
}
2014-02-10 16:28:50 -06:00
2014-02-10 14:55:18 -06:00
sort($Hashes);
$CalculatedChecksum = md5(implode(',', $Hashes));
if($CalculatedChecksum != $MetaData->MD5) {
return FALSE;
}
else {
return TRUE;
}
}
/**
2014-02-10 15:18:28 -06:00
* Returns a list of all files in a directory, recursively (Thanks @businessdad)
2014-02-10 14:55:18 -06:00
*
2014-02-10 15:18:28 -06:00
* @param string Directory The directory to scan for files
2014-02-10 14:55:18 -06:00
* @result array A list of Files and, optionally, Directories.
*/
2014-02-10 15:18:28 -06:00
protected function _GetFiles($Directory) {
2014-02-10 14:55:18 -06:00
$Files = array_diff(scandir($Directory), array('.', '..'));
$Result = array();
foreach($Files as $File) {
$FileName = $Directory . '/' . $File;
if(is_dir($FileName)) {
2014-02-10 15:18:28 -06:00
$Result = array_merge($Result, $this->_GetFiles($FileName));
2014-02-10 16:28:50 -06:00
continue;
2014-02-10 14:55:18 -06:00
}
$Result[] = $FileName;
}
return $Result;
}
}