nano-tetra-modules/Themes/api/module.php

573 lines
20 KiB
PHP

<?php namespace pineapple;
/*
* Author: trashbo4t (github.com/trashbo4t)
*/
class Themes extends Module
{
// CONSTANTS
private $MODULE_DIR = '/pineapple/modules/Themes/';
private $CSS_DIR = '/pineapple/modules/Themes/css/';
private $SKELETON_CSS = '/pineapple/modules/Themes/css/main.css';
private $CURRENT_CSS = '/pineapple/modules/Themes/css/CURRENT_CSS';
private $CURRENT_LOGO = '/pineapple/modules/Themes/img/CURRENT_LOGO';
private $CURRENT_THROBBER = '/pineapple/modules/Themes/img/CURRENT_THROBBER';
private $CURRENT_FAVICON = '/pineapple/modules/Themes/img/CURRENT_FAVICON';
private $CURRENT_DASHBOARD = '/pineapple/modules/Themes/img/CURRENT_DASHBOARD';
private $BACKUP_MAIN_CSS = '/pineapple/css/.backup.main.css';
private $BACKUP_FAVICON = '/pineapple/img/.backup.favicon.ico';
private $BACKUP_THROBBER = '/pineapple/img/.backup.throbber.gif';
private $BACKUP_LOGO = '/pineapple/img/.backup.logo.png';
private $BACKUP_DASHBOARD = '/pineapple/modules/Dashboard/.backup.module_icon.svg';
private $current_theme = "";
/*
* Static list of all module names
* from here we can grab the CURRENT file, and the module directory
*/
private $ALL_MODULES = array(
"Dashboard",
"Recon",
"Clients",
"ModuleManager",
"Filters",
"PineAP",
"Tracking",
"Logging",
"Reporting",
"Networking",
"Configuration",
"Advanced",
"Notes",
"Help",
);
/*
* Faster to map Hex values then to calculate on the fly
*/
private $WHITE_HEX = 'ffffff';
private $RESTORE_HEX = '808080';
private $NORMAL_COLOR_HEX_MAP = array(
"red" => "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_<MODULE_ICON> 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
*
* This is a neat little technique to change image colors, since the images are SVG we search and
* replace the original RBG colors in the image file with the new RBG hex values.
*/
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;
}
}