"FF0000", "green" => "00FF00", "blue" => "0000FF", "purple" => "800080", "orange" => "cc3300", "yellow" => "ffff00", "pink" => "ff0066", ); private $LIGHT_COLOR_HEX_MAP = array( "red" => "ff4d4d", "green" => "80ff80", "blue" => "8080ff", "purple" => "ff66ff", "orange" => "ff9f80", "yellow" => "ffff66", "pink" => "ff99c2", ); private $DARK_COLOR_HEX_MAP = array( "red" => "990000", "green" => "004d00", "blue" => "000077", "purple" => "4d004d", "orange" => "992600", "yellow" => "cccc00", "pink" => "99003d", ); public function route() { switch ($this->request->action) { case 'getThemeList': $this->handleGetThemeList(); break; case 'themeFields': $this->getThemeFields(); break; case 'deleteTheme': $this->handleDeleteTheme(); break; case 'activateTheme': $this->activateTheme(); break; case 'getThemeCode': $this->getThemeCode(); break; case 'submitThemeCode': $this->submitThemeCode(); break; case 'getCurrentTheme': $this->getCurrentTheme(); break; case 'createNewTheme': $this->handleCreateNewTheme(); break; case 'restoreDefault': $this->restoreDefault(); break; case 'backupFiles': $this->backupFiles(); break; case 'replaceImage': $this->replaceImage(); break; } } // Get the CURRENT_ file, which is 1 line with the current color of the icon public function currentFile($name) { $upper = strtoupper($name); return "/pineapple/modules/Themes/img/CURRENT_{$upper}"; } // Move an image from light->dark or vice versa public function replaceImage() { $img = $this->request->img; switch ($img) { // Pineapple Logo case 'Logo': $this->response = array("message" => "Logo Changed"); if ($this->request->light) { exec("cp $this->BACKUP_LOGO /pineapple/img/logo.png"); exec("echo light > $this->CURRENT_LOGO"); } else { exec("echo dark > $this->CURRENT_LOGO"); exec('cp /pineapple/modules/Themes/img/logo-dark.png /pineapple/img/logo.png'); } $this->response = array("message" => "Logo Changed"); break; // Pineapple favicon.ico Image case 'Icon': if ($this->request->light) { exec("echo light > $this->CURRENT_FAVICON"); exec("cp $this->BACKUP_FAVICON /pineapple/img/favicon.ico"); } else { exec("echo dark > $this->CURRENT_FAVICON"); exec('cp /pineapple/modules/Themes/img/favicon-dark.ico /pineapple/img/favicon.ico'); } $this->response = array("message" => "Icon Changed"); break; // Pineapple Throbber gif case 'Throbber': if ($this->request->light) { exec("echo light > $this->CURRENT_THROBBER"); exec("cp $this->BACKUP_THROBBER /pineapple/img/throbber.gif"); } else { exec("echo dark > $this->CURRENT_THROBBER"); exec('cp /pineapple/modules/Themes/img/throbber-dark.gif /pineapple/img/throbber.gif'); } $this->response = array("message" => "Throbber Changed"); break; // Modify all of the module Icons case 'All': foreach ($this->ALL_MODULES as $module) { $current = $this->currentFile($module); $success = $this->replaceModuleImage( $module, $this->request->color, $this->request->brightness ); } $this->response = array( "success" => true, "message" => "All module icons changed to {$this->request->color}-{$this->request->brightness}" ); break; // Assume module Icon default: $success = $this->replaceModuleImage( $this->request->img, $this->request->color, $this->request->brightness ); $this->response = array( "success" => $success, "message" => "{$this->request->img} icon changed to {$this->request->color}-{$this->request->brightness}" ); break; } } /* * replaceModuleImage * $moduleName -> String name of module, can be any format (nEtWoRkIng) because it gets formatted * $color -> string name of the color, used for index of mapping * $brightness -> string name of brightness, used for map selection */ public function replaceModuleImage($moduleName, $color, $brightness) { $current = $this->currentFile($moduleName); $replace = "/pineapple/modules/{$moduleName}/module_icon.svg"; switch($color) { case 'light': return $this->restoreModuleIcon ( $moduleName ); break; case 'dark': if (exec("echo dark > $current") != 0 || exec("echo $brightness >> $current") != 0 || !$this->searchAndReplaceFile($replace, "FFFFFF")) { return false; } break; default: $hex = ""; switch($brightness) { case 'light': $hex = $this->LIGHT_COLOR_HEX_MAP[$color]; break; case 'dark': $hex = $this->DARK_COLOR_HEX_MAP[$color]; break; default: $hex = $this->NORMAL_COLOR_HEX_MAP[$color]; break; } // Replace the modules icon image if (exec("echo $color > $current") != 0 || exec("echo $brightness >> $current") != 0) { return false; } if (!$this->searchAndReplaceFile($replace, $hex)) { return false; } break; } return true; } /* * searchAndReplaceFile * $s -> substring to find * return: true or false showing succcessful string replacement */ public function searchAndReplaceFile($f, $s) { // Use a stream editor so we dont have to load the entire file into RAM return (exec("sed -i 's/fill:\(.*\);/fill:#{$s};/g' $f") == 0); } /* * setCurrentTheme * $theme -> modify CURRENT_CSS file with new theme */ public function setCurrentTheme($theme) { $this->current_theme = $theme; exec('echo '.$theme.' > /pineapple/modules/Themes/css/CURRENT_CSS'); } /* * getCurrentTheme * return current theme, and all parameters for icon colors/brightness */ public function getCurrentTheme() { $line = file('/pineapple/modules/Themes/css/CURRENT_CSS')[0]; $line = trim(preg_replace('/\s+/', ' ', $line)); $logo = file('/pineapple/modules/Themes/img/CURRENT_LOGO')[0]; $logo = trim(preg_replace('/\s+/', ' ', $logo)); $icon = file('/pineapple/modules/Themes/img/CURRENT_FAVICON')[0]; $icon = trim(preg_replace('/\s+/', ' ', $icon)); $throbber = file('/pineapple/modules/Themes/img/CURRENT_THROBBER')[0]; $throbber = trim(preg_replace('/\s+/', ' ', $throbber)); $this->response = array( "current" => $line, "logo" => $logo, "icon" => $icon, "throbber" => $throbber, ); foreach ($this->ALL_MODULES as $module) { $current = $this->currentFile($module); $lower = strtolower($module); $color = file($current)[0]; $color = trim(preg_replace('/\s+/', ' ', $color)); $brightness = file($current)[1]; $brightness = trim(preg_replace('/\s+/', ' ', $brightness)); $this->response[$lower] = $color; $this->response[$lower.'brightness'] = $brightness; } } /* * isCurrentThemeEnv * $theme string name of theme to check if its current * check if global current_them var is set, compare against that * this way we dont open,read,close a file every for every check */ public function isCurrentThemeEnv($theme) { if ($this->current_theme != "") { return ($this->current_theme == $theme); } if (!file_exists($this->CURRENT_CSS)) { return false; } $line = file($this->CURRENT_CSS)[0]; $line = trim(preg_replace('/\s+/', ' ', $line)); return ($line === $theme); } /* * restoreImages * Undo any changes made by this Module * This includes: original icons, gifs, svg's */ public function restoreImages() { $success = true; exec("cp {$this->BACKUP_FAVICON} /pineapple/img/favicon.ico"); exec("cp {$this->BACKUP_LOGO} /pineapple/img/logo.png"); exec("cp {$this->BACKUP_THROBBER} /pineapple/img/throbber.gif"); exec('echo light > /pineapple/modules/Themes/img/CURRENT_LOGO'); exec('echo light > /pineapple/modules/Themes/img/CURRENT_FAVICON'); exec('echo light > /pineapple/modules/Themes/img/CURRENT_THROBBER'); foreach ($this->ALL_MODULES as $module) { $current = $this->currentFile($module); $success = $this->restoreModuleIcon ( $module ); } $this->response = array( "success" => $success, "message" => "Restored all files" ); } /* * restoreModuleIcon * Generic helper function to put a modules icon back to normal * using only the name of the module (in any format). */ public function restoreModuleIcon($moduleName) { $current = $this->currentFile($moduleName); $replace = "/pineapple/modules/{$moduleName}/module_icon.svg"; if (!$this->searchAndReplaceFile($replace, $this->RESTORE_HEX)) { return false; } if (exec("echo light > $current") != 0 || exec("echo normal >> $current") != 0) { return false; } return true; } /* * restoreDefault * backup all files if not done yet * put the original css file back * restore all of the images */ public function restoreDefault() { $this->backupFiles(); exec("cp {$this->BACKUP_MAIN_CSS} /pineapple/css/main.css"); $this->setCurrentTheme('main.css'); $this->restoreImages(); } /* * getThemeCode * retrieve the css styling code from a theme file */ public function getThemeCode() { $code = file_get_contents($this->CSS_DIR . $this->request->name); $this->response = array("code" => $code, "file" => $this->CSS_DIR . $this->request->name); } /* * getThemeFields * more or less only returns the code for now */ public function getThemeFields() { $allFields = array(); $code = file_get_contents($this->CSS_DIR . $this->request->name); $this->response = array("code" => $code); } /* * activateTheme * mv the users selected theme to main.css file */ public function activateTheme() { $themeName = $this->request->name; $cmd = exec("cp {$this->CSS_DIR}{$themeName} /pineapple/css/main.css"); if ($cmd == 0) { $this->setCurrentTheme($themeName); $message = $themeName . " is now active."; $this->response = array("return" => true, "message" => $message); } else { $message = "Could not move theme" . $themeName . "(Something is wrong..)"; $this->response = array("return" => false,"message" => $message); } } /* Credits to SteveRusin at http://php.net/manual/en/ref.strings.php */ private function endsWith($str, $sub) { return (substr($str, strlen($str) - strlen($sub)) === $sub); } /* * handleDeleteTheme * delete a users theme file from the local css directory */ public function handleDeleteTheme() { $themeName = $this->request->name; exec("rm {$this->CSS_DIR}{$themeName}"); if (!file_exists("/pineapple/modules/Themes/css/" . $themeName)) { $message = "Deleted " . $themeName; } else { $message = "Error deleting " . $themeName; } $this->response = array("message" => $message); } /* * submitThemeCode * save a users theme file in the local css directory */ public function submitThemeCode() { $code = $this->request->themeCode; $themeName = $this->request->name; $fileName = $this->request->fileName; file_put_contents($this->CSS_DIR . $themeName, $code); $message = (!file_exists($this->CSS_DIR . $themeName)) ? "Created " . $themeName : "Updated " . $themeName; $this->response = array( "message" => $message, "filename" => $fileName ); } /* * handleGetThemeList * get the list of .css files in the local css directory * avoid sending back the main.css file so it cannot be modified */ public function handleGetThemeList() { $all_themes = array(); $root_themes = preg_grep('/^([^.])/', scandir("{$this->CSS_DIR}")); foreach ($root_themes as $theme) { if (!is_file($theme) && $this->endsWith($theme, '.css') && $theme != "main.css") { $active = $this->isCurrentThemeEnv($theme); $obj = array("title" => $theme, "location" => "../Themes/css/", "active" => $active); array_push($all_themes, $obj); } } $this->response = $all_themes; } /* * handleCreateNewTheme * create a new .css theme file in the local css directory */ public function handleCreateNewTheme() { $themePath = $this->CSS_DIR; $themeName = str_replace(' ', '_', $this->request->themeName); if (!$this->endswith($themeName, '.css')) { $themeName = $themeName . ".css"; } if (file_exists($themePath . $themeName)) { $this->response = array("create_success" => false, "create_message" => "A theme named {$themeName} already exists."); return; } exec("cp {$this->SKELETON_CSS} {$themePath}{$themeName}"); $this->response = array("create_success" => true, "create_message" => "Created {$themeName}"); } /* * backupFiles * Backup all of the .css/IMG files used so the module can properly restore defaults */ public function backupFiles() { $success = true; $modules = array(); if (!file_exists($this->BACKUP_MAIN_CSS)) { exec("cp /pineapple/css/main.css {$this->BACKUP_MAIN_CSS}"); array_push($modules, "Backed up main.css."); } if (!file_exists($this->SKELETON_CSS)) { mkdir($this->CSS_DIR); exec("cp {$this->BACKUP_MAIN_CSS} {$this->SKELETON_CSS}"); array_push($modules, "Backed up skeleton.css."); } if (!file_exists($this->BACKUP_THROBBER)) { exec("cp /pineapple/img/throbber.gif {$this->BACKUP_THROBBER}"); array_push($modules, "Backed up favicon.ico"); } if (!file_exists($this->CURRENT_THROBBER)) { exec("echo light > $this->CURRENT_THROBBER"); array_push($modules, "Wrote to {$this->CURRENT_THROBBER}"); } if (!file_exists($this->BACKUP_FAVICON)) { exec("cp /pineapple/img/favicon.ico {$this->BACKUP_FAVICON}"); array_push($modules, "Backed up favicon.ico"); } if (!file_exists($this->CURRENT_FAVICON)) { exec("echo light > $this->CURRENT_FAVICON"); array_push($modules, "Wrote to /pineapple/modules/Themes/img/CURRENT_FAVICON"); } if (!file_exists($this->BACKUP_LOGO)) { exec("cp /pineapple/img/logo.png $this->BACKUP_LOGO"); array_push($modules, "Wrote to {$this->BACKUP_LOGO}"); } if (!file_exists($this->CURRENT_LOGO)) { exec("echo light > $this->CURRENT_LOGO"); array_push($modules, "Wrote to {$this->CURRENT_LOGO}"); } foreach ($this->ALL_MODULES as $module) { $current = $this->currentFile($module); if (!$this->backupModuleIcon($current)) { array_push($modules, "Did not write to {$current}."); } else { array_push($modules, "Wrote to {$current}."); } } $this->response = array( "success" => $success, "message" => $success ? "Created a backup file for all files" : "Failed to backup files! Tread lightly", "modules" => $modules ); } public function backupModuleIcon($currentFile) { if (!file_exists($currentFile)) { if (exec("echo light > $currentFile") != 0 || exec("echo normal >> $currentFile") != 0) { return false; } return true; } return false; } }