diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..fd4c0ee21 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "packages/core/apps/workbench"] + path = packages/core/apps/workbench/sources + url = https://github.com/f7ed0/equal-workbench.git + branch = dev diff --git a/packages/core/actions/config/create-view.php b/packages/core/actions/config/create-view.php index b6b8b89cc..e769b22f4 100644 --- a/packages/core/actions/config/create-view.php +++ b/packages/core/actions/config/create-view.php @@ -56,7 +56,7 @@ throw new Exception("view_id_invalid",QN_ERROR_INVALID_PARAM); } -if(strcmp($type, "form")!==0 && strcmp($type, "list")!==0) { +if(strcmp($type, "form")!==0 && strcmp($type, "list")!==0 && strcmp($type, "search")!==0 && strcmp($type, "app")!==0 && strcmp($type, $package)!==0 ) { $test = strcmp($type, "list"); throw new Exception("view_type_invalid",QN_ERROR_INVALID_PARAM); } @@ -85,10 +85,10 @@ throw new Exception('file_access_denied', QN_ERROR_UNKNOWN); } -if($type == "form") { +if($type == "form" || $type == "search") { fputs($f,"{\"layout\" : {\"groups\" : []}}"); } -elseif($type == "list") { +else { fputs($f,"{\"layout\" : {\"items\" : []}}"); } diff --git a/packages/core/actions/config/update-controller.php b/packages/core/actions/config/update-controller.php index 9e9571c48..1c7c31f1c 100644 --- a/packages/core/actions/config/update-controller.php +++ b/packages/core/actions/config/update-controller.php @@ -22,6 +22,7 @@ 'controller' => [ 'description' => 'Name of the controller.', 'type' => 'string', + 'usage' => 'orm/entity' // 'required' => true ], 'operation' => [ @@ -55,8 +56,8 @@ $prettyPrinter = new PhpParser\PrettyPrinter\Standard; // Get the parts of the entity string, separated by backslashes -$params['entity'] = str_replace('_', '\\', $params['entity']); -$parts = explode('\\', $params['entity']); +$params['controller'] = str_replace('_', '\\', $params['controller']); +$parts = explode('\\', $params['controller']); // Get the package name from the first part of the string $package = array_shift($parts); @@ -65,9 +66,12 @@ // Get the class path from the remaining part $class_path = implode('/', $parts); +/*if(!($decoded = json_decode($params['payload'],true))) { + throw new Exception('Malformed Json', QN_ERROR_INVALID_PARAM); +}*/ // Get a string representation from the code_php variable, with backslashes escaped -$code_string = str_replace("\\\\", "\\", var_export($params['payload'], true)); +$code_string = str_replace("\\\\", "\\", var_export( $params['payload'], true)); // #test #toremove // $code_string = "[ @@ -137,9 +141,10 @@ public function leaveNode(Node $node) { ); // Get the full path of the file -$dir = ['do' => 'actions', 'get' => 'date', 'show' => 'apps'][$params['operation']]; +$dir = ['do' => 'actions', 'get' => 'data', 'show' => 'apps'][$params['operation']]; $file = QN_BASEDIR."/packages/{$package}/{$dir}/{$class_path}/{$filename}.php"; +$file = str_replace("//","/",$file); // Get the code from the original file ... $code = file_get_contents($file); // ... and parse it to create an AST @@ -174,7 +179,8 @@ public function leaveNode(Node $node) { } } catch(Exception $e) { - trigger_error("PHP::unable to beautify rendered file ($file): ".$e->getMessage(), QN_REPORT_INFO); + throw new Exception('unable to beautfy the file', QN_ERROR_UNKNOWN); + //trigger_error("PHP::unable to beautify rendered file ($file): ".$e->getMessage(), QN_REPORT_INFO); } $result = file_get_contents($file); diff --git a/packages/core/apps/workbench/.gitignore b/packages/core/apps/workbench/.gitignore deleted file mode 100644 index eef52883d..000000000 --- a/packages/core/apps/workbench/.gitignore +++ /dev/null @@ -1 +0,0 @@ -source/ diff --git a/packages/core/apps/workbench/manifest.json b/packages/core/apps/workbench/manifest.json old mode 100644 new mode 100755 index 73da546c0..c0f34d874 --- a/packages/core/apps/workbench/manifest.json +++ b/packages/core/apps/workbench/manifest.json @@ -1,13 +1,9 @@ { "name": "Workbench", - "description": "App for customizing models, views and controllers from the user interface without code.", - "version": "1.0", - "authors": ["Cedric Francoys", "Sylvain Sausse", "Quentin Leveque"], - "license": "LGPL-3", - "repository": "https://github.com/equalframework/apps-core-settings.git", + "description": "This is the workbench application.", "url": "/workbench", "icon": "edit_note", - "color": "#0f1397", + "color": "#d2252c", "access": { "groups": [ "users", "admin" diff --git a/packages/core/apps/workbench/sources b/packages/core/apps/workbench/sources new file mode 160000 index 000000000..2efc9f222 --- /dev/null +++ b/packages/core/apps/workbench/sources @@ -0,0 +1 @@ +Subproject commit 2efc9f2227c9786368b9c981084e466a02fbf7bf diff --git a/packages/core/apps/workbench/web.app b/packages/core/apps/workbench/web.app index be2b6f8fc..222b96e52 100644 Binary files a/packages/core/apps/workbench/web.app and b/packages/core/apps/workbench/web.app differ diff --git a/packages/core/data/config/menus.php b/packages/core/data/config/menus.php new file mode 100644 index 000000000..1f2675e8a --- /dev/null +++ b/packages/core/data/config/menus.php @@ -0,0 +1,98 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU LGPL 3 license +*/ +list($params, $providers) = eQual::announce([ + 'description' => 'Returns the list of menus defined in a given package, or applicable to a given entity.', + 'response' => [ + 'content-type' => 'application/json', + 'charset' => 'UTF-8', + 'accept-origin' => '*' + ], + 'params' => [ + 'package' => [ + 'description' => 'Name of the package for which the list is requested.', + 'type' => 'string', + 'required' => true + ], + ], + 'providers' => ['context', 'orm'] +]); + +/** + * @var \equal\php\Context $context + * @var \equal\orm\ObjectManager $orm + */ +list($context, $orm) = [$providers['context'], $providers['orm']]; + +$result = []; + +if(!file_exists("packages/{$params['package']}")) { + throw new Exception('missing_package_dir', QN_ERROR_INVALID_CONFIG); +} +if(!file_exists("packages/{$params['package']}/views")) { + throw new Exception('missing_views_dir', QN_ERROR_INVALID_CONFIG); +} +// recurse through all sub-folders of `views` directory +$result = recurse_dir("packages/{$params['package']}/views", 'json', $params['package']); + + +$context->httpResponse() + ->body($result) + ->send(); + +function has_sub_items($directory, $extension) { + $files = glob($directory.'/*.'.$extension); + if(count($files)) { + return true; + } + foreach(glob($directory.'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $node) { + if(has_sub_items($node, $extension)) { + return true; + } + } + return false; +} + +/** + * #memo - this method slightly differs from the one in controllers.php and translations.php + */ +function recurse_dir($directory, $extension, $parent_name='') { + $result = array(); + if( is_dir($directory) ) { + $dir_name = basename($directory); + $list = glob($directory.'/*'); + foreach($list as $node) { + $filename = basename($node, '.'.$extension); + list($entity_name, $view_id) = explode('.', $filename, 2); + if(!($entity_name == 'menu')) continue; + if(is_dir($node)) { + if(!has_sub_items($node, $extension)) { + continue; + } + $result = array_merge($result, recurse_dir($node, $extension, (strlen($parent_name)?$parent_name.'\\'.$filename:$filename))); + } + elseif(pathinfo($node, PATHINFO_EXTENSION) == $extension) { + $entity = (strlen($parent_name)?$parent_name.'\\':'').$entity_name; + try { + // #memo - ! can be controller or class + // $entity::getType(); + $result[] = $view_id; + } + catch(Exception $e) { + if($entity_name == 'menu') { + // ignore + } + else { + // entity should be a controller + // #todo - check that the controller actually exists + $result[] = str_replace('\\', '_', $entity).':'.$view_id; + } + } + } + } + } + return $result; +} diff --git a/packages/core/data/config/types.php b/packages/core/data/config/types.php index 93535393e..fb18f4fdf 100755 --- a/packages/core/data/config/types.php +++ b/packages/core/data/config/types.php @@ -136,7 +136,10 @@ 'computed' => [ 'type' => ['type' => 'string'], //'default' => ['type' => 'string'], - 'function', 'result_type', 'onupdate', 'store', 'instant', 'multilang', 'selection' + 'function', 'result_type', 'onupdate', 'store', 'instant', 'multilang' + ], + 'array' => [ + 'default','type' => ['type' => 'string'],'dependencies','onupdate', 'selection','usage' ] ]; diff --git a/packages/core/data/config/views.php b/packages/core/data/config/views.php index 5823a3585..02272597c 100644 --- a/packages/core/data/config/views.php +++ b/packages/core/data/config/views.php @@ -132,6 +132,7 @@ function recurse_dir($directory, $extension, $parent_name='') { foreach($list as $node) { $filename = basename($node, '.'.$extension); list($entity_name, $view_id) = explode('.', $filename, 2); + if($entity_name == 'menu') continue; if(is_dir($node)) { if(!has_sub_items($node, $extension)) { continue; diff --git a/packages/core/data/console.php b/packages/core/data/console.php new file mode 100644 index 000000000..17f5a1ef9 --- /dev/null +++ b/packages/core/data/console.php @@ -0,0 +1,196 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU LGPL 3 license +*/ + +list($params, $providers) = eQual::announce([ + 'description' => 'Returns a descriptor of current installation Settings, holding specific values for current User, if applicable.', + 'access' => [ + 'visibility' => 'public' + ], + 'params' => [ + 'thread_id' => [ + 'type' => 'string', + 'description' => 'Thread_id of the line' + ], + 'level' => [ + 'type' => 'string', + 'description' => 'Level of the WARNING | DEBUG | INFO | ERROR' + ], + 'mode' => [ + 'type' => 'string', + 'description' => 'php | orm | sql | api | app' + ], + 'time' => [ + 'type' => 'string', + 'description' => 'Indicates the time of the log' + + ], + 'mtime' => [ + 'type' => 'string', + 'description' => 'Mtime allows to look for a precise time' + + ], + 'help' => [ + 'type' => 'boolean', + 'description' => 'Set to true to display help' + ], + 'limit' => [ + 'type' => 'integer', + 'description' => 'Returns the selected number of lines' + ] + ], + 'response' => [ + 'content-type' => 'application/json', + 'charset' => 'UTF-8', + 'accept-origin' => '*' + ], + 'providers' => ['context', 'orm', 'auth'] +]); + + +list($context, $om, $auth) = [$providers['context'], $providers['orm'], $providers['auth']]; + +/** + * @var string $level + * @return string ANSI escape codes to change the color of the level text according to their values + * example $level = "WARNING" returns yellow color + */ +function calColor(string $level) +{ + $green = "\e[32;1m"; + $red = "\e[31;1m"; + $blue = "\e[34;1m"; + $yellow = "\e[33;1m"; + $white = "\e[0m"; + + if (is_null($level)) return $white; + switch (strtoupper($level)) { + case 'WARNING': + case E_USER_WARNING: + return $yellow; + case 'DEBUG': + case E_USER_DEPRECATED: + return $green; + case 'INFO': + case 'NOTICE': + case E_USER_NOTICE: + return $blue; + case 'ERROR': + case 'FATAL': + case 'Fatal error': + case 'Parse error': + return $red; + default: + return $white; + } +} + +/** + * Displays a thread + * @var Array $thread + */ +function displayThread(array $thread) +{ + $green = "\e[32;1m"; + $red = "\e[31;1m"; + $white = "\e[0m"; + $bold = "\e[00;1m"; + $text = ""; + + $text .= "$green ${thread['time']} $white"; + if ($thread['mtime']) { + $text .= "$bold {$thread['mtime']} $white"; + } + if (is_string($thread['level'])) { + $text .= calColor($thread['level']) . "[${thread['level']}]$white"; + } + $text .= " ${thread['mode']}"; + $text .= "${bold} ${thread['function']} "; + $text .= "${white}@ ${thread['file']} : "; + $text .= "line $bold${thread['line']}$white | "; + $text .= "thread_id $red ${thread['thread_id']} $white"; + if (is_string($thread['message'])) { + // check message format to display in lines if it is an associative array + if (is_array(json_decode($thread['message'], true))) { + $newMessage = json_decode($thread['message'], true); + foreach ($newMessage as $val) { + if (is_array($val)) { + $m = ""; + foreach ($val as $id => $v) { + $m .= "$white ${bold}${id}${white} : \e[3m${v} \e[23m"; + } + $text .= "\n${bold}message:$m"; + } else if (is_string($val)) { + $text .= "${bold}\nmessage:$white \e[3m${val} \e[23m"; // message displays in italics + } + } + } else { + $text .= "$bold \nmessage:$white \e[3m${thread['message']} \e[23m"; + } + // message displays in italics + } + if (isset($thread['stack'])) { + for ($i = 0; $i < count($thread['stack']); $i++) { + $stack = $thread['stack'][count($thread['stack']) - $i - 1]; + $text .= $i == count($thread['stack']) - 1 ? "\n └ " : "\n ├ "; + $text .= "${stack['function']} @ ${stack['file']} ${stack['line']} $white"; + } + } + return ($text); +} + +/** + * Filters a thread arguments are given in params + * @return Array $thread | null + */ +function filterThreadByParams(array $thread, array $params) +{ + if (isset($params['mode']) && $params['mode'] !== '' && $thread['mode'] == strtoupper($params['mode'])) { + return $thread; + } + if (isset($params['level']) && isset($params['level']) != '' && $thread['level'] == strtoupper($params['level'])) { + return $thread; + }; + if (isset($params['thread_id']) && $params['thread_id'] != '' && $thread['thread_id'] == $params['thread_id']) { + return $thread; + } + if (isset($params['mtime']) && $params['mtime'] != '' && $thread['mtime'] == $params['mtime']) { + return $thread; + } + if (isset($params['time']) && $params['time'] != '' && str_contains(($thread['time']), $params['time'])) { + return $thread; + } + if (!isset($params['time']) && !isset($params['mtime']) && !isset($params['thread_id']) && !isset($params['level']) && !isset($params['mode'])) { + return $thread; + } +} + +if (file_exists('/var/www/html/log/eq_error.log')) { + // read raw data from pointer log file + $fp = fopen("/var/www/html/log/eq_error.log", "r"); + echo "START LOG\n"; + $cpt = 0; + if ($fp) { + while ((($data = stream_get_line($fp, 65535, PHP_EOL)) !== false) && ((isset($params["limit"]) && $cpt <= $params["limit"]) || !isset($params["limit"]))) { + + $thread = json_decode($data, true); + if (!is_null($thread)) { + $filteredThread = filterThreadByParams($thread, $params); + if (!is_null($filteredThread)) { + print(displayThread($thread)); + echo ("\n---------------------------------------------------------------------------------------------------------------------------------------------\n"); + }; + } + $cpt++; + } + fclose($fp); + } + echo "\nEND LOG \e[0m \n"; +}; + +// $context->httpResponse() +// ->body($text) +// ->send();