diff --git a/Conjuro/Config.php b/Conjuro/Config.php index 0720ce9..da1e1f5 100644 --- a/Conjuro/Config.php +++ b/Conjuro/Config.php @@ -6,13 +6,13 @@ "error-page"=>"error", "login-page"=>"login", - // mySQL/mariaDB Database connection settings - "db-host"=>"localhost", - "db-name"=>"conjuro", - "db-user"=>"root", - "db-pass"=>"toor", - "db-charset"=>"utf8mb4", - "db-prefix"=>"cj-", + // webDB (Mongo Data API) Database connection settings + "db-engine"=>"dirDB", + "db-path"=>"content/data", + "db-endpoint"=>"https://eu-west-2.aws.data.mongodb-api.com/app/data-vxnpo/endpoint/data/v1", + "db-key"=>"P7AIqDN7EnqA1fbAcawovEYgtf1qv1ix6DRU6dTSXOMP2zKCXghpFNhekGefrKET", + "db-data-source"=>"Cluster-Paris", + "db-name"=>"Conjuro", // Site defaults "site-title"=>"Conjuro", diff --git a/Conjuro/Conjuro.php b/Conjuro/Conjuro.php index 5ebaaaf..dd42670 100644 --- a/Conjuro/Conjuro.php +++ b/Conjuro/Conjuro.php @@ -22,8 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - - include_once "ezDBclass.php"; class Conjuro{ private $db; @@ -41,31 +39,30 @@ //////////////////////////////////////////////////////////////////// /** + * Insetrs into db lig + */ + private function DBLog($user,$action,$type,$info){ + $this->db()->insert('user-log',['time'=>date('Y-m-d H:i:s', time()),'user'=>$user,'action'=>$action,'type'=>$type,'info'=>$info]); + } + + /** * Select a list from te database */ - public function DBlist($type,$fields=null,$where='',$types = null,&...$data){ - return $this->db()->select($type,$fialds,$where,$types,...$data); + public function DBlist($type,$filter=false){ + $result=$this->db()->list($type,$filter); + $this->DBLog($_SESSION['user']['user'],'LIST',$type,['filter'=>$filter,'count'=>count($result)]); + return $result; } /** * Return single entity from DB */ public function DBget($type,$key=""){ - if ($key!="") - $data=$this->db()->select($type,null,'where id=?','i',$key); - else - $data=$this->db()->select($type); + $data=$this->db()->get($type,$key); foreach($data as $i => $row){ $data[$i]['$type']=$type; - $types=[]; - foreach($row as $k => $v){ - if ($v===null) - $types[$k]='!'; - else - $types[$k]=is_string($v)?'s':'i'; - } - $data[$i]['$types']=$types; } + $this->DBLog($_SESSION['user']['user'],'GET',$type,['id'=>$id]); return $data; } @@ -74,27 +71,14 @@ if ($type=='') // data is not entity return; $id=$data['id']; - $dbdata=$this->db()->select($type,null,'where id=?','i',$id); + $dbdata=$this->db()->get($type,$id); if (count($dbdata)==0){ - $fields=''; - $types=''; - $values=[]; - foreach($data as $i => $v){ - if ($i[0]=='$') - continue; - if (strlen($fields)>0) - $fields.=','; - $fields.=$i; - $types.=$data['$types'][$i]; - $values[]=$v; - } - if ($fields!=''){ - $this->db->insert($type,$fields,$types,...$values); + die(var_Dump($this->db->insert($type,$data))); $data['id']=$this->db->insert_id; - $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'INSERT',$type,$this->db->insert_id); - } + DBLog($_SESSION['user']['user'],'INSERT',$type,$data['id']); }else{ $dbdata=$dbdata[0]; // Select the first row + die("compose a good chnge detetor or use replace"); $set=''; $types=''; $values=[]; @@ -112,8 +96,8 @@ $types.='i'; $values[]=$id; if ($set!=''){ - $this->db->update($type,$set,'where id=?',$types,...$values); - $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'UPDATE',$type,$id); + $this->db()->update($type,$set,'where id=?',$types,...$values); + $this->DBLog('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'UPDATE',$type,$id); } } return $data; @@ -131,15 +115,25 @@ return; $id=$data['id']; } - $this->db()->delete($type,'where id=?','i',$id); - $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'DELETE',$type,$id); + $this->db()->delete($type,$id); + $this->DBLog($_SESSION['user']['user'],'DELETE',$type,$id); return $data; } // Database private function db(){ - if(!isset($this->db)) - $this->db=new DB($this->conf); + if(!isset($this->db)){ + switch($this->conf["db-engine"]){ + case "dirDB": + include_once "dirDB.php"; + $this->db=new dirDB($this->conf["db-path"]); + break; + case "webDB": + include_once "webDB.php"; + $this->db=new webDB($this->conf["db-endpoint"],$this->conf["db-name"],$this->conf["db-key"],$this->conf["db-data-source"]); + break; + } + } return $this->db; } @@ -158,7 +152,7 @@ public function login($usr,$pwd){ $_SESSION['user']=null; // Reset previous login if any $usr=strtolower($usr); - $user=$this->db()->selectOne('users',null,'where user=? and pass=sha1(?) and active=true','ss',$usr,$pwd); + $user=$this->DBget('users',["user"=>$usr]);// TODO check active field. $user['user']=strtolower($user['user']); if ($user) return $_SESSION['user']=$user; @@ -173,6 +167,7 @@ * Checks the current login information */ public function checkLogin(){ + return true; if ($_SESSION['user']) // refresh user data return $_SESSION['user']=$this->db()->selectOne('users',null,'where id=? and active=true','i',$_SESSION['user']['id']); // Check for bootstrap/recover users @@ -307,6 +302,13 @@ echo $this->htmlClose($tag); } + /** + * Return true is debig mode is set. + */ + public function isDebug(){ + return $this->conf["debug"]; + } + } /** diff --git a/Conjuro/dirDB.php b/Conjuro/dirDB.php new file mode 100644 index 0000000..536b01f --- /dev/null +++ b/Conjuro/dirDB.php @@ -0,0 +1,79 @@ +path=$path; + } + + public function get($table,$id){ + $base=$this->path; + return $this->read("$base/$table/$id.json"); + } + + public function list($table,$filter=false){ + $base=$this->path; + $result=[]; + foreach (glob("$base/$table/*.json") as $file) { + $result[]=$this->read($file); + } + return $result; + } + + public function insert($table,$data,$many=false){ + $url=$this->url; + if ($many){ + $this->call("${url}/action/insertMany",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "document"=>$data,// <- in this case $data should be a simepl array of associative arrays + ]); + }else{ + $this->call("${url}/action/insertOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "document"=>$data, + ]); + } + } + + public function update($table,$changes){ + $this->call("${url}/action/updateOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "filter"=>[ "_id"=>$id ], + "update"=>[ "$set"=>$changes ], + ]); + } + + public function replace($table,$data){ + $this->call("${url}/action/replaceOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "filter"=>[ "_id"=>$id ], + "replacement"=>$data, + ]); + } + + public function delete($table,$id){ + $this->call("${url}/action/deleteOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "filter"=>[ "_id"=>$id ], + ]); + } + + function read($path){ + $result=file_get_contents($path); + if ($result===false) + return null; + return json_decode($result,true); + } + } + +?> \ No newline at end of file diff --git a/Conjuro/ezDBclass.php b/Conjuro/ezDBclass.php index cbbbfd7..c1cdd93 100644 --- a/Conjuro/ezDBclass.php +++ b/Conjuro/ezDBclass.php @@ -177,4 +177,102 @@ } } +class DBEx(){ + /** + * Return single entity from DB + */ + public function DBget($type,$key=""){ + if ($key!="") + $data=$this->db()->select($type,null,'where id=?','i',$key); + else + $data=$this->db()->select($type); + foreach($data as $i => $row){ + $data[$i]['$type']=$type; + $types=[]; + foreach($row as $k => $v){ + if ($v===null) + $types[$k]='!'; + else + $types[$k]=is_string($v)?'s':'i'; + } + $data[$i]['$types']=$types; + } + return $data; + } + + public function DBsave($data){ + $type=$data['$type']; + if ($type=='') // data is not entity + return; + $id=$data['id']; + $dbdata=$this->db()->select($type,null,'where id=?','i',$id); + if (count($dbdata)==0){ + $fields=''; + $types=''; + $values=[]; + foreach($data as $i => $v){ + if ($i[0]=='$') + continue; + if (strlen($fields)>0) + $fields.=','; + $fields.=$i; + $types.=$data['$types'][$i]; + $values[]=$v; + } + if ($fields!=''){ + $this->db->insert($type,$fields,$types,...$values); + $data['id']=$this->db->insert_id; + $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'INSERT',$type,$this->db->insert_id); + } + }else{ + $dbdata=$dbdata[0]; // Select the first row + $set=''; + $types=''; + $values=[]; + foreach($data as $i => $v){ + if ($i[0]=='$') + continue; + if ($dbdata[$i]!=$v){// only differences + if (strlen($set)>0) + $set.=','; + $set.="$i = ?"; + $types.=$data['$types'][$i]; + $values[]=$v; + } + } + $types.='i'; + $values[]=$id; + if ($set!=''){ + $this->db->update($type,$set,'where id=?',$types,...$values); + $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'UPDATE',$type,$id); + } + } + return $data; + } + + // Delete an entity, use DBdelete() or DBdelete(,) + public function DBdelete($data,$id=null){ + if (is_string($data)){ + if ($id){ + $type=$data; + } + }else{ + $type=$data['$type']; + if ($type=='') // data is not entity + return; + $id=$data['id']; + } + $this->db()->delete($type,'where id=?','i',$id); + $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'DELETE',$type,$id); + return $data; + } + + // Database + private function db(){ + if(!isset($this->db)) + $this->db=new webDB($this->conf); + return $this->db; + } +} + ?> \ No newline at end of file diff --git a/Conjuro/webDB.php b/Conjuro/webDB.php new file mode 100644 index 0000000..b379105 --- /dev/null +++ b/Conjuro/webDB.php @@ -0,0 +1,112 @@ +url=$url; + $this->db=$db; + $this->key=$key; + $this->dataSource=$dataSource; + } + + public function get($table,$id){ + $url=$this->url; + return $this->call("${url}/action/findOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "filter"=>[ "_id"=>$id ], + ]); + } + + public function list($table,$filter=false){ + $url=$this->url; + if ($filter===false) + return $this->call("${url}/action/find",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + ])["documents"]; + else + return $this->call("${url}/action/find",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "filter"=>$filter, + ])["documents"]; + } + + public function insert($table,$data,$many=false){ + $url=$this->url; + if ($many){ + $this->call("${url}/action/insertMany",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "document"=>$data,// <- in this case $data should be a simepl array of associative arrays + ]); + }else{ + $this->call("${url}/action/insertOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "document"=>$data, + ]); + } + } + + public function update($table,$changes){ + $this->call("${url}/action/updateOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "filter"=>[ "_id"=>$id ], + "update"=>[ "$set"=>$changes ], + ]); + } + + public function replace($table,$data){ + $this->call("${url}/action/replaceOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "filter"=>[ "_id"=>$id ], + "replacement"=>$data, + ]); + } + + public function delete($table,$id){ + $this->call("${url}/action/deleteOne",[ + "dataSource"=>$this->dataSource, + "database"=>$this->db, + "collection"=>$table, + "filter"=>[ "_id"=>$id ], + ]); + } + + function call($url,$data){ + $key=$this->key; + $options = [ + 'http' => [ + 'header' => "Content-type: application/json\r\nAccept: application/json\r\napiKey: ${key}\r\n", + 'method' => 'POST', + 'content' => json_encode($data), + ], + ]; + + $context = stream_context_create($options); + $result = file_get_contents($url, false, $context); + if ($result === false) { + /* Handle error */ + $error = error_get_last(); + throw new Exception("Mongo error: " . $error['message']); + die(); + } + return json_decode($result,true); + } + } + +?> \ No newline at end of file diff --git a/content/controls/common-admin.php b/content/controls/common-admin.php index 23fd677..330f72d 100644 --- a/content/controls/common-admin.php +++ b/content/controls/common-admin.php @@ -41,5 +41,45 @@ +
+ +
+
+
+
Archivo base (layout):
+
Archivo base sobre la que se configura la página.
+ +
+
+
Controladores de página:
+
Modulos a ejecutar al inicio de la página, los módilos se ejecutaran en el orden indicado.
+
+
+
+
+ +
+
+
+
+
Librerías de cotroles
+
Librerías de controles a cargar, los controles disponibles dependen de las librarias cargadas.
+
+
+
+ +
+
+
+
+
+ \ No newline at end of file diff --git a/content/controls/common.php b/content/controls/common.php index 3844d61..b0c746b 100644 --- a/content/controls/common.php +++ b/content/controls/common.php @@ -23,11 +23,6 @@ ); } }, - new class('builder') extends Control{ - protected function renderControl($meta,$data) { - include "Conjuro/Builder.php"; - } - }, new class('form') extends Control{ protected function renderControl($meta,$data) { $cols=$meta['columns']; @@ -105,6 +100,7 @@ protected function renderControl($meta,$data) { $itemData=$data[$meta['data']]; $itemTemplate=$meta['item']; + $headerTemplate=$meta['header']; if (!$itemData) $itemData=[]; ?> @@ -133,7 +129,22 @@ $link.='&'; }else $link='?'; - $link.='data='.urlencode(json_encode($itemData)); + $linkdata=null; + $linkname; + if (isset($meta['link-field'])){ + $linkname=$meta['link-field']; + $linkdata=$itemData[$meta['link-field']]; + }else if (count($itemData)>0){ + $linkname='data'; + $linkdata=$itemData; + } + if($linkdata){ + if (is_object($linkdata) || is_array($linkdata)) + $linkdata=urlencode(json_encode($linkdata)); + else + $linkdata=urlencode($linkdata); + $link.="${linkname}=${linkdata}"; + } ?>
  • <?=$this->conf("site-title")?> + +
    @@ -13,12 +15,36 @@
    data('username')?>
    showContent('panel');?>
    -
    -
    +
    +
    showContent('header');?>
    showContent('body');?>
    +conf("debug")){ +?> + +
    [show page data]
    + + \ No newline at end of file diff --git a/content/modules/Admin.php b/content/modules/Admin.php index 06e5194..a4ab111 100644 --- a/content/modules/Admin.php +++ b/content/modules/Admin.php @@ -23,17 +23,23 @@ SOFTWARE. */ return new class($conf) extends Module{ + /** + * Array of pages + */ private function listPages(){ - $result=[]; - foreach($this->recursivedir('content/pages','*.json') as $page) - $result[]=['name'=>$page]; - return $result; + return $this->recursivedir('content/pages','*.json'); } + /** + * Array of users + */ private function listUsers(){ - return c()->DBlist('users','id,user,name,created_on,active'); + return c()->DBlist('users'); } + /** + * Tool for recursive directory listings + */ private function recursivedir($dir,$pattern,$cut=-1){ if ($cut<0) $cut=strlen($dir)+1; @@ -58,19 +64,24 @@ ]; }); $this->register('controller-page-list',function($page){ - $page->setData('pages',$this->listPages()); + $pageList=[]; + foreach($this->listPages() as $p){ + $pageList[]=['id'=>$p]; + } + $page->setData('page-list',$pageList); }); $this->register('controller-page-edit',function($page){ - $edata=$_GET['data']; - //$page->setData('pages',$this->listPages()); + $pname=$_GET['id']; + $page->setData('page-id',$pname); + $page->setData('page',Conjuro::loadJson("content/pages/$pname")); }); $this->register('controller-user-list',function($page){ - $page->setData('users',$this->listUsers()); + //$page->setData('users',$this->listUsers()); }); $this->register('controller-user-edit',function($page){ - $edata=json_decode($_GET['data'],true); - $page->setData('user-id',$edata['id']); - $page->setData('user-name',$edata['name']); + $inData=json_decode($_GET['data'],true); + $page->setData('user-id',$inData['id']); + $page->setData('user-name',$inData['name']); }); } }?> \ No newline at end of file diff --git a/content/modules/Page.php b/content/modules/Page.php index 2bc6381..5eab0c1 100644 --- a/content/modules/Page.php +++ b/content/modules/Page.php @@ -94,6 +94,13 @@ } /** + * Gets the meta dictionary of the page + */ + public function getAllMeta(){ + return $this->meta; + } + + /** * Show the content defined for a section on page meta */ public function showContent($name){ @@ -153,9 +160,10 @@ $page=$this->getPage($this->currentPage()); if ($page->needAuth() && !$_SESSION['user']){ $page=$this->getPage($this->conf["login-page"]); + }else{ + $page->setData('username',$_SESSION['user']['name']); + $page->setData('userid',$_SESSION['user']['id']); } - $page->setData('username',$_SESSION['user']['name']); - $page->setData('userid',$_SESSION['user']['id']); $page->showPage(); } diff --git a/content/pages/admin/dashboard.json b/content/pages/admin/dashboard.json index 7d06ec4..d703b37 100644 --- a/content/pages/admin/dashboard.json +++ b/content/pages/admin/dashboard.json @@ -21,7 +21,12 @@ "body": [ { "@": "admin-tile", - "html": "Welcome to Conjuro!!" + "content": [ + { + "@": "html", + "html": "Welcome to Conjuro!!" + } + ] } ] } \ No newline at end of file diff --git a/content/pages/admin/page-edit.json b/content/pages/admin/page-edit.json index 168d10e..2cb34ec 100644 --- a/content/pages/admin/page-edit.json +++ b/content/pages/admin/page-edit.json @@ -27,15 +27,10 @@ "body": [ { "@": "admin-tile", + "title":"Editando {page-id}", "content": [ { - "@": "list", - "data":"pages", - "item":{ - "@":"list-item", - "link":"?page=admin/edit", - "field-title":"name" - } + "@": "builder" } ] } diff --git a/content/pages/admin/pages.json b/content/pages/admin/pages.json index 45e6eaf..1db6b56 100644 --- a/content/pages/admin/pages.json +++ b/content/pages/admin/pages.json @@ -30,11 +30,12 @@ "content": [ { "@": "list", - "data":"pages", + "data":"page-list", "item":{ "@":"list-item", "link":"?page=admin/page-edit", - "field-title":"name" + "field-title":"id", + "link-field":"id" } } ] diff --git a/content/pages/admin/users.json b/content/pages/admin/users.json index a2e589f..d49ec1f 100644 --- a/content/pages/admin/users.json +++ b/content/pages/admin/users.json @@ -35,6 +35,12 @@ "@":"list-item", "link":"?page=admin/user-edit", "field-title":"name" + }, + "header":{ + "@":"admin-list-header", + "items":[ + + ] } } ] diff --git a/index.php b/index.php index 9180207..d6d2921 100644 --- a/index.php +++ b/index.php @@ -35,6 +35,12 @@ try{ m($module)->do(); }catch (Exception $e) { - echo "
    ".$e->getMessage()."
    "; + $msg="
    "; + $msg.=$e->getMessage(); + if (c()->isDebug()){ + $msg.="
    ".$e->getTraceAsString()."
    "; + } + $msg.="
    "; + echo $msg; } ?> \ No newline at end of file diff --git a/media/admin/builder.js b/media/admin/builder.js new file mode 100644 index 0000000..1013902 --- /dev/null +++ b/media/admin/builder.js @@ -0,0 +1,23 @@ +/* Builder class for conjuro + * Copyright 2023 XWolfOverride + * + * Licensed under the MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +var builder = new (function () { + +})(); \ No newline at end of file diff --git a/media/admin/style.css b/media/admin/style.css index 90718b8..f497f6c 100644 --- a/media/admin/style.css +++ b/media/admin/style.css @@ -5,18 +5,21 @@ --color-text-hover: white; --scroll-bar-color: #909aad; --scroll-bar-bg-color: transparent; - --color-control-bg:#b5c0da; - --color-control-dark:#838ea8; - --color-highlight:#9449d1; + --color-control-bg: #b5c0da; + --color-control-dark: #838ea8; + --color-highlight: #9449d1; + + --panel-width: 16rem; } /* Layout */ html, -body{ +body { margin: 0; - font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; width: 100%; - height: 100%; + height: 100%; + position: relative; + overflow: hidden; } html { @@ -25,29 +28,30 @@ } body { - width: 100%; - height: 100%; - margin: 0; - position: relative; - overflow: hidden; + display: flex; + flex-direction: row; } -html,body,td,input,select{ +html, +body, +td, +input, +select { font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; - color:var(--color-text); + color: var(--color-text); } #panel { - position: fixed; background-color: #fafaff; color: var(--color-text); overflow-x: hidden; overflow-y: auto; - width: 15rem; - height: 100%; + width: var(--panel-width); box-shadow: 0 0 7px #00000055; padding: 0.5rem; z-index: 10; + display: flex; + flex-direction: column; } #panel>#panel-title { @@ -65,23 +69,16 @@ padding-bottom: 4rem; } -#content { +#workarea { position: relative; background-color: #dbe3e6; background-image: linear-gradient(#c3cfd3, #dbe3e6); background-repeat: no-repeat; background-size: 100% 130px; - width: calc(100% - 15rem); - height: 100%; - margin-left: 15rem; overflow-y: auto; overflow-x: hidden; } -#content-main { - min-height: calc(100% - 2rem); -} - #content-header { background-color: #dbe3e6aa; background-image: linear-gradient(#c3cfd3aa, #dbe3e6aa); @@ -94,7 +91,7 @@ right: 0; height: 4rem; z-index: 9; - padding: 0 1rem 0 16rem; + padding: 0 1rem 0 calc(var(--panel-width) + 1rem); backdrop-filter: blur(5px); line-height: 4rem; } @@ -126,8 +123,14 @@ } /* Admin-Panel */ + +#panel-content { + display: flex; + flex-direction: column; + gap: 1rem; +} + .admin-panel-button { - padding: 0.5rem 0; color: var(--color-text); } @@ -147,6 +150,47 @@ padding: 0 1rem 0 2rem; } +/* Debug info */ +#debug-info { + position: fixed; + bottom: 1rem; + left: 1rem; + z-index: 100; + cursor: pointer; + font-size: 0.6em; +} + +#debug-info:hover { + color: #3232ff; +} + +#debug-info-panel { + position: fixed; + z-index: 101; + top: 2rem; + left: 2rem; + bottom: 2rem; + right: 2rem; + background-color: white; + border-radius: 1rem; + box-shadow: 0 0 1rem black; + padding: 1rem; +} + +#debug-info-panel #full-scroll { + overflow-x: hidden; + overflow-y: scroll; + width:100%; + height: 100%; +} + +#debug-info-panel pre{ + background-color: #333333; + color:white; + font-size: 0.8em; + padding:0.5rem; +} + /* Admin common*/ .btn-ico { @@ -157,57 +201,87 @@ } .btn-ico:hover { - color:var(--color-text-hover); + color: var(--color-text-hover); background-color: var(--color-control-bg); } +input{ + border:1px solid #9c9c9c; + border-radius: 0.5rem; + padding: 0.3rem 1rem; + border-bottom: 2px solid #9c9c9c;; +} + +input:focus{ + outline: 3px solid #792cb6; + outline-offset: 1px; +} + /* Admin title*/ -.admin-title{ - float:left; +.admin-title { + float: left; } -.admin-title-right{ - float:right; + +.admin-title-right { + float: right; +} + +/* control::builder */ +.page-builder .caption{ + text-decoration: underline; +} + +.page-builder .info{ + font-size: 0.7em; + /*background-color: #fefff0;*/ + margin:0.2rem 0; + padding:0.3rem; + border-radius: 0.2rem; + color:#333333; } /* Admin tile*/ -.admin-tile{ +.admin-tile { background-color: white; - border-radius: 0.3rem; - box-shadow: 0 0 7px #00000033; + border-radius: 0.5rem; + box-shadow: 0 0 7px #00000033; + overflow: hidden; } -.admin-tile.small{ - display:inline-block; +.admin-tile.small { + display: inline-block; } -.admin-tile-header{ +.admin-tile-header { font-size: 0.9rem; padding: 0.5rem 1.5rem; + background: #f1f1f1; } -.admin-tile-body{ - padding:0.5rem 1.5rem 1.5rem 1.5rem; + +.admin-tile-body { + padding: 0.5rem 1.5rem 1.5rem 1.5rem; } /* control::list */ -ul.list{ +ul.list { margin: 0; padding-inline-start: 0; } -ul.list li{ - display:block; - padding:0.5rem 1rem; +ul.list li { + display: block; + padding: 0.5rem 1rem; cursor: pointer; border-radius: 3px; } -ul.list li:hover{ +ul.list li:hover { background-color: var(--color-control-bg); - background-image: linear-gradient(130deg,var(--color-control-bg) 80%, var(--color-highlight) 100%); - color:var(--color-text-hover); + background-image: linear-gradient(130deg, var(--color-control-bg) 80%, var(--color-highlight) 100%); + color: var(--color-text-hover); } @@ -265,4 +339,84 @@ background-color: var(--scroll-bar-color); border-radius: 20px; border: 3px solid var(--scroll-bar-bg-color); +} + +.layout { + display: flex; +} + +.layout.vertical { + flex-direction: column; +} + +.layout.horizontal { + flex-direction: row; +} + +.layout.gap { + gap: 0.7rem; +} + +.layout.big-gap { + gap: 1rem; +} + +.layout.horizontal.reverse { + flex-direction: row-reverse; +} + +.layout.vertical.reverse { + flex-direction: column-reverse; +} + +.main { + flex: 1; +} + +.layout.horizontal.left { + justify-content: flex-start; +} + +.layout.horizontal.center { + justify-content: center; +} + +.layout.horizontal.right { + justify-content: flex-end; +} + +.layout.horizontal.top { + align-items: flex-start; +} + +.layout.horizontal.middle { + align-items: center; +} + +.layout.horizontal.bottom { + align-items: flex-end; +} + +.layout.vertical.left { + align-items: flex-start; +} + +.layout.vertical.center { + align-items: center; +} + +.layout.vertical.right { + align-items: flex-end; +} + +.layout.vertical.top { + justify-content: flex-start; +} + +.layout.vertical.middle { + justify-content: center; +} + +.layout.vertical.bottom { + justify-content: flex-end; } \ No newline at end of file diff --git a/media/muWeb.js b/media/muWeb.js new file mode 100644 index 0000000..54c33e3 --- /dev/null +++ b/media/muWeb.js @@ -0,0 +1,139 @@ +/* µWeb 1.0 Web development micro-framework + * Copyright 2022 XWolfOverride + * + * Licensed under the MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +var µWeb = (function () { + + /** + * Show dialog + * The dialog is a div element on the page with default styling for hidden and position + * Id's on the dialog should start with the same id base in order to use the get funciton + * @param {string} did Dialog base id + * @param {function} fninit init function + * @param {*} buttons button definition and logic. + * @returns Dialog API + */ + function dialog(did, fninit, buttons) { + var api = { + get: id => { + if (!id) + return dialog; + var dom = document.getElementById(did + "-" + id); + if (!dom) + console.error("Dialog '" + did + "': element '" + did + "-" + k + "' not found."); + return dom; + }, + close: () => showDialog(false), + }, dialog = document.getElementById(did); + function showDialog(show) { + var cssfnc = show ? "add" : "remove"; + document.getElementById("wall").classList[cssfnc]("show"); + dialog.classList[cssfnc]("show"); + } + + fninit && fninit(api); + function installButtonEvent(dom, event) { + dom.onclick = () => event(api); + } + for (var k in buttons) + installButtonEvent(api.get(k), buttons[k]); + showDialog(true); + return api; + } + + /** + * Generates a repetition of elements from a source of data + * @param {dom} template Template dom + * @param {function} fncreated Element created callback + * @param {scting} idBase base id of repetition for each iteration (only for elements with id) + * @returns Repeater instance API + */ + function repeater(template, fncreated, idBase) { + var target, rows; + + if (!idBase) + idBase = "repeater"; + + function feed(data) { + clear(); + rows = []; + for (var i in data) { + var d = data[i]; + var r = template.cloneNode(true); + populate(r, d, idBase + ":" + i + "::", r); + r._repeater_data = d; + r._repeater_index = i; + rows.push(r); + fncreated && fncreated(r, d); + target.appendChild(r); + } + } + function clear() { + if (!rows) + return; + rows.forEach(r => { + r.parentElement.removeChild(r); + }); + } + + // == Configuration + function setTemplate(templ) { + template = templ + if (templ.parentElement) { + target = templ.parentElement; + templ.parentElement.removeChild(templ); + } + } + function setTarget(trg) { + target = trg; + } + setTemplate(template); + return { + feed: feed, + clear: clear, + setTemplate: setTemplate, + setTarget: setTarget, + } + } + + function populate(dom, data, idBase, link) { + if (dom.id && idBase) + dom.id = idBase + dom.id; + dom._link = link; + // process dataset: + for (var k in dom.dataset) { + var d = data[dom.dataset[k]]; + if (d === undefined) d = ""; + if (k == '#') + dom.innerText = d; + else + dom[k] = d; + } + // go to childs + for (var i in dom.children) + populate(dom.children[i], data, idBase, link); + } + + return { + dialog: dialog, + repeater: repeater, + populate: populate + } +})();