Overview

Packages

  • None
  • Yaga

Classes

  • ActedModel
  • ActionController
  • ActionModel
  • AwardCombo
  • BadgeAwardModel
  • BadgeController
  • BadgeModel
  • BadgesController
  • BadgesModule
  • BestController
  • BestFilterModule
  • CakeDayPost
  • CommentCount
  • CommentMarathon
  • DiscussionBodyLength
  • DiscussionCategory
  • DiscussionCount
  • DiscussionPageCount
  • HasMentioned
  • HolidayVisit
  • LeaderBoardModule
  • LengthOfService
  • ManualAward
  • NecroPost
  • NewbieComment
  • PhotoExists
  • PostCount
  • PostReactions
  • QnAAnserCount
  • RankController
  • RankModel
  • ReactController
  • ReactionCount
  • ReactionModel
  • ReflexComment
  • RulesController
  • SocialConnection
  • Yaga
  • YagaController
  • YagaHooks

Interfaces

  • YagaRule
  • Overview
  • Package
  • Class
  • Tree
  • Todo
  • Download
  1: <?php if(!defined('APPLICATION')) exit();
  2: /* Copyright 2013 Zachary Doll */
  3: 
  4: /**
  5:  * Manage the yaga application including configuration and import/export
  6:  *
  7:  * @since 1.0
  8:  * @package Yaga
  9:  */
 10: class YagaController extends DashboardController {
 11: 
 12:   /**
 13:    * @var array These objects will be created on instantiation and available via
 14:    * $this->ObjectName
 15:    */
 16:   public $Uses = array('Form');
 17: 
 18:   /**
 19:    * Make this look like a dashboard page and add the resources
 20:    *
 21:    * @since 1.0
 22:    * @access public
 23:    */
 24:   public function Initialize() {
 25:     parent::Initialize();
 26:     $this->Application = 'Yaga';
 27:     Gdn_Theme::Section('Dashboard');
 28:     if($this->Menu) {
 29:       $this->Menu->HighlightRoute('/yaga');
 30:     }
 31:     $this->AddSideMenu('yaga/settings');
 32: 
 33:     $this->AddCssFile('yaga.css');
 34:   }
 35: 
 36:   /**
 37:    * Redirect to settings by default
 38:    */
 39:   public function Index() {
 40:     $this->Settings();
 41:   }
 42: 
 43:   /**
 44:    * This handles all the core settings for the gamification application.
 45:    */
 46:   public function Settings() {
 47:     $this->Permission('Garden.Settings.Manage');
 48:     $this->Title(T('Yaga.Settings'));
 49: 
 50:     // Get list of actions from the model and pass to the view
 51:     $ConfigModule = new ConfigurationModule($this);
 52: 
 53:     $ConfigModule->Initialize(array(
 54:         'Yaga.Reactions.Enabled' => array(
 55:             'LabelCode' => 'Use Reactions',
 56:             'Control' => 'Checkbox'
 57:         ),
 58:         'Yaga.Badges.Enabled' => array(
 59:             'LabelCode' => 'Use Badges',
 60:             'Control' => 'Checkbox'
 61:         ),
 62:         'Yaga.Ranks.Enabled' => array(
 63:             'LabelCode' => 'Use Ranks',
 64:             'Control' => 'Checkbox'
 65:         ),
 66:         'Yaga.LeaderBoard.Enabled' => array(
 67:             'LabelCode' => 'Show leaderboard on activity page',
 68:             'Control' => 'Checkbox'
 69:         ),
 70:         'Yaga.LeaderBoard.Limit' => array(
 71:             'LabelCode' => 'Maximum number of leaders to show',
 72:             'Control' => 'Textbox',
 73:             'Options' => array(
 74:                 'Size' => 45,
 75:                 'class' => 'SmallInput'
 76:             )
 77:         )
 78:     ));
 79:     $this->ConfigurationModule = $ConfigModule;
 80: 
 81:     $this->Render();
 82:   }
 83: 
 84:   /**
 85:    * Import a Yaga transport file
 86:    */
 87:   public function Import() {
 88:     $this->Title(T('Yaga.Import'));
 89:     $this->SetData('TransportType', 'Import');
 90:     
 91:     if(!class_exists('ZipArchive')) {
 92:       $this->Form->AddError(T('Yaga.Error.TransportRequirements'));
 93:     }
 94:     
 95:     if($this->Form->IsPostBack() == TRUE) {
 96:       // Handle the file upload
 97:       $Upload = new Gdn_Upload();
 98:       $TmpZip = $Upload->ValidateUpload('FileUpload', FALSE);
 99: 
100:       $ZipFile = FALSE;
101:       if($TmpZip) {
102:         // Generate the target name
103:         $TargetFile = $Upload->GenerateTargetName(PATH_UPLOADS, 'zip');
104:         $BaseName = pathinfo($TargetFile, PATHINFO_BASENAME);
105: 
106:         // Save the uploaded zip
107:         $Parts = $Upload->SaveAs($TmpZip, $BaseName);
108:         $ZipFile = PATH_UPLOADS . DS . $Parts['SaveName'];
109:         $this->SetData('TransportPath', $ZipFile);
110:       }
111: 
112:       $Include = $this->_FindIncludes();
113:       if(count($Include)) {
114:         $Info = $this->_ExtractZip($ZipFile);
115:         $this->_ImportData($Info, $Include);
116:         Gdn_FileSystem::RemoveFolder(PATH_UPLOADS . DS . 'import' . DS . 'yaga');
117:       }
118:       else {
119:         $this->Form->AddError(T('Yaga.Error.Includes'));
120:       }
121:     }
122:     
123:     if($this->Form->ErrorCount() == 0 && $this->Form->IsPostBack()) {
124:       $this->Render('transport-success');
125:     }
126:     else {
127:       $this->Render();
128:     }
129:   }
130: 
131:   /**
132:    * Create a Yaga transport file
133:    */
134:   public function Export() {
135:     $this->Title(T('Yaga.Export'));
136:     $this->SetData('TransportType', 'Export');
137: 
138:     if(!class_exists('ZipArchive')) {
139:       $this->Form->AddError(T('Yaga.Error.TransportRequirements'));
140:     }
141: 
142:     if($this->Form->IsPostBack()) {
143:       $Include = $this->_FindIncludes();
144:       if(count($Include)) {
145:         $Filename = $this->_ExportData($Include);
146:         $this->SetData('TransportPath', $Filename);
147:       }
148:       else {
149:         $this->Form->AddError(T('Yaga.Error.Includes'));
150:       }
151:     }
152: 
153:     if($this->Form->ErrorCount() == 0 && $this->Form->IsPostBack()) {
154:       $this->Render('transport-success');
155:     }
156:     else {
157:       $this->Render();
158:     }
159:   }
160: 
161:   /**
162:    * This searches through the submitted checkboxes and constructs an array of
163:    * Yaga sections to be included in the transport file.
164:    * 
165:    * @return array
166:    */
167:   protected function _FindIncludes() {
168:     $FormValues = $this->Form->FormValues();
169:     $Sections = $FormValues['Checkboxes'];
170: 
171:     // Figure out which boxes were checked
172:     $Include = array();
173:     foreach($Sections as $Section) {
174:       $Include[$Section] = $FormValues[$Section];
175:     }
176:     return $Include;
177:   }
178:   
179:   /**
180:    * Creates a transport file for easily transferring Yaga configurations across
181:    * installs
182:    *
183:    * @param array An array containing the config areas to transfer
184:    * @param string Where to save the transport file
185:    * @return mixed False on failure, the path to the transport file on success
186:    */
187:   protected function _ExportData($Include = array(), $Path = NULL) {
188:     $StartTime = microtime(TRUE);
189:     $Info = new stdClass();
190:     $Info->Version = C('Yaga.Version', '?.?');
191:     $Info->StartDate = date('Y-m-d H:i:s');
192: 
193:     if(is_null($Path)) {
194:       $Path = PATH_UPLOADS . DS . 'export' . date('Y-m-d-His') . '.yaga.zip';
195:     }
196:     $FH = new ZipArchive();
197:     $Images = array();
198:     $Hashes = array();
199: 
200:     if($FH->open($Path, ZipArchive::CREATE) !== TRUE) {
201:       $this->Form->AddError(sprintf(T('Yaga.Error.ArchiveCreate'), $FH->getStatusString()));
202:       return FALSE;
203:     }
204: 
205:     // Add configuration items
206:     $Info->Config = 'configs.yaga';
207:     $Configs = Gdn::Config('Yaga', array());
208:     unset($Configs['Version']);
209:     $ConfigData = serialize($Configs);
210:     $FH->addFromString('configs.yaga', $ConfigData);
211:     $Hashes[] = md5($ConfigData);
212:     
213:     // Add actions
214:     if($Include['Action']) {
215:       $Info->Action = 'actions.yaga';
216:       $Actions = Yaga::ActionModel()->Get('Sort', 'asc');
217:       $this->SetData('ActionCount', count($Actions));
218:       $ActionData = serialize($Actions);
219:       $FH->addFromString('actions.yaga', $ActionData);
220:       $Hashes[] = md5($ActionData);
221:     }
222: 
223:     // Add ranks and associated image
224:     if($Include['Rank']) {
225:       $Info->Rank = 'ranks.yaga';
226:       $Ranks = Yaga::RankModel()->Get('Level', 'asc');
227:       $this->SetData('RankCount', count($Ranks));
228:       $RankData = serialize($Ranks);
229:       $FH->addFromString('ranks.yaga', $RankData);
230:       array_push($Images, C('Yaga.Ranks.Photo'), NULL);
231:       $Hashes[] = md5($RankData);
232:     }
233: 
234:     // Add badges and associated images
235:     if($Include['Badge']) {
236:       $Info->Badge = 'badges.yaga';
237:       $Badges = Yaga::BadgeModel()->Get();
238:       $this->SetData('BadgeCount', count($Badges));
239:       $BadgeData = serialize($Badges);
240:       $FH->addFromString('badges.yaga', $BadgeData);
241:       $Hashes[] = md5($BadgeData);
242:       foreach($Badges as $Badge) {
243:         array_push($Images, $Badge->Photo);
244:       }
245:     }
246: 
247:     // Add in any images
248:     $FilteredImages = array_filter($Images);
249:     $ImageCount = count($FilteredImages);
250:     $this->SetData('ImageCount', $ImageCount);
251:     if($ImageCount > 0) {
252:       $FH->addEmptyDir('images');
253:     }
254: 
255:     foreach($FilteredImages as $Image) {
256:       if($FH->addFile(PATH_UPLOADS . DS . $Image, 'images' . DS . $Image) !== TRUE) {
257:         $this->Form->AddError(sprintf(T('Yaga.Error.AddFile'), $FH->getStatusString()));
258:         return FALSE;
259:       }
260:       $Hashes[] = md5_file(PATH_UPLOADS . DS . $Image);
261:     }
262: 
263:     // Save all the hashes
264:     sort($Hashes);
265:     $Info->MD5 = md5(implode(',', $Hashes));
266:     $Info->EndDate = date('Y-m-d H:i:s');
267: 
268:     $EndTime = microtime(TRUE);
269:     $TotalTime = $EndTime - $StartTime;
270:     $m = floor($TotalTime / 60);
271:     $s = $TotalTime - ($m * 60);
272: 
273:     $Info->ElapsedTime = sprintf('%02d:%02.2f', $m, $s);
274: 
275:     $FH->setArchiveComment(serialize($Info));
276:     if($FH->close()) {
277:       return $Path;
278:     }
279:     else {
280:       $this->Form->AddError(sprintf(T('Yaga.Error.ArchiveSave'), $FH->getStatusString()));
281:       return FALSE;
282:     }
283:   }
284: 
285:   /**
286:    * Extract the transport file and validate
287:    *
288:    * @param string The transport file path
289:    * @return boolean Whether or not the transport file was extracted successfully
290:    */
291:   protected function _ExtractZip($Filename) {
292:     if(!file_exists($Filename)) {
293:       $this->Form->AddError(T('Yaga.Error.FileDNE'));
294:             return FALSE;
295:         }
296: 
297:     $ZipFile = new ZipArchive();
298:     $Result = $ZipFile->open($Filename);
299:     if($Result !== TRUE) {
300:       $this->Form->AddError(T('Yaga.Error.ArchiveOpen'));
301:       return FALSE;
302:     }
303: 
304:     // Get the metadata from the comment
305:     $Comment = $ZipFile->comment;
306:     $MetaData = unserialize($Comment);
307: 
308:     $Result = $ZipFile->extractTo(PATH_UPLOADS . DS . 'import' . DS . 'yaga');
309:     if($Result !== TRUE) {
310:       $this->Form->AddError(T('Yaga.Error.ArchiveExtract'));
311:       return FALSE;
312:     }
313: 
314:     $ZipFile->close();
315: 
316:     // Validate checksum
317:     if($this->_ValidateChecksum($MetaData) === TRUE) {
318:       return $MetaData;
319:     }
320:     else {
321:       $this->Form->AddError(T('Yaga.Error.ArchiveChecksum'));
322:       return FALSE;
323:     }
324:   }
325: 
326:   /**
327:    * Overwrites Yaga configurations, dumps Yaga db tables, inserts data via the 
328:    * model, and copies uploaded files to the server
329:    * 
330:    * @param stdClass The info object read in from the archive
331:    * @param array Which tables should be overwritten
332:    * @return bool Pass/Fail on the import being executed. Errors can exist on the
333:    * form with a passing return value.
334:    */
335:   protected function _ImportData($Info, $Include) {
336:     if(!$Info) {
337:       return FALSE;
338:     }
339:     
340:     // Import Configs
341:     $Configs = unserialize(file_get_contents(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $Info->Config));
342:     $Configurations = $this->_NestedToDotNotation($Configs, 'Yaga');
343:     foreach($Configurations as $Name => $Value) {
344:       SaveToConfig($Name, $Value);
345:     }
346:     
347:     // Import model data
348:     foreach($Include as $Key => $Value) {
349:       if($Value) {
350:         $Data = unserialize(file_get_contents(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $Info->$Key));
351:         Gdn::SQL()->EmptyTable($Key);
352:         $ModelName = $Key . 'Model';
353:         $Model = Yaga::$ModelName();
354:         foreach($Data as $Datum) {
355:           $Model->Insert((array)$Datum);
356:         }
357:         $this->SetData($Key . 'Count', $Model->GetCount());
358:       }
359:     }
360:     
361:     // Import uploaded files
362:     if(Gdn_FileSystem::Copy(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . 'images' . DS, PATH_UPLOADS . DS) === FALSE) {
363:       $this->Form->AddError(T('Yaga.Error.TransportCopy'));
364:     }
365:     
366:     return TRUE;
367:   }
368:   
369:   /**
370:    * Converted a nest config array into an array where indexes are the configuration
371:    * strings and the value is the value
372:    * 
373:    * @param array The nested array
374:    * @param string What should the configuration strings be prefixed with
375:    * @return array
376:    */
377:   protected function _NestedToDotNotation($Configs, $Prefix = '') {
378:     $ConfigStrings = array();
379:     
380:     foreach($Configs as $Name => $Value) {
381:       if(is_array($Value)) {
382:         $ConfigStrings = array_merge($ConfigStrings, $this->_NestedToDotNotation($Value, "$Prefix.$Name"));
383:       }
384:       else {
385:         $ConfigStrings["$Prefix.$Name"] = $Value;
386:       }
387:     }
388:     
389:     return $ConfigStrings;
390:   }
391: 
392:   /**
393:    * Inspects the Yaga transport files and calculates a checksum
394:    *
395:    * @param stdClass The metadata object read in from the transport file
396:    * @return boolean Whether or not the checksum is valid
397:    */
398:   protected function _ValidateChecksum($MetaData) {
399:     $Hashes = array();
400:     
401:     // Hash the config file
402:     $Hashes[] = md5_file(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $MetaData->Config);
403:     
404:     // Hash the data files
405:     if(property_exists($MetaData, 'Action')) {
406:       $Hashes[] = md5_file(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $MetaData->Action);
407:     }
408: 
409:     if(property_exists($MetaData, 'Badge')) {
410:       $Hashes[] = md5_file(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $MetaData->Badge);
411:     }
412: 
413:     if(property_exists($MetaData, 'Rank')) {
414:       $Hashes[] = md5_file(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . $MetaData->Rank);
415:     }
416: 
417:     // Hash the image files
418:         $Files = $this->_GetFiles(PATH_UPLOADS . DS . 'import' . DS . 'yaga' . DS . 'images');
419:     $this->SetData('ImageCount', count($Files));
420:         foreach($Files as $File) {
421:             $Hashes[] = md5_file($File);
422:         }
423: 
424:     sort($Hashes);
425:         $CalculatedChecksum = md5(implode(',', $Hashes));
426:    
427:     if($CalculatedChecksum != $MetaData->MD5) {
428:       return FALSE;
429:         }
430:         else {
431:       return TRUE;
432:         }
433:   }
434: 
435:   /**
436:      * Returns a list of all files in a directory, recursively (Thanks @businessdad)
437:      *
438:      * @param string Directory The directory to scan for files
439:      * @return array A list of Files and, optionally, Directories.
440:      */
441:     protected function _GetFiles($Directory) {
442:     $Files = array_diff(scandir($Directory), array('.', '..'));
443:     $Result = array();
444:     foreach($Files as $File) {
445:       $FileName = $Directory . '/' . $File;
446:       if(is_dir($FileName)) {
447:           $Result = array_merge($Result, $this->_GetFiles($FileName));
448:           continue;
449:       }
450:       $Result[] = $FileName;
451:     }
452:     return $Result;
453:   }
454: 
455: }
456: 
Yaga API documentation generated by ApiGen 2.8.0