diff --git a/index.php b/index.php index ed52b50e..4864db53 100644 --- a/index.php +++ b/index.php @@ -38,3 +38,4 @@ if($Url->whereAmI()==='admin') { else { require(PATH_BOOT.'site.php'); } + diff --git a/install.php b/install.php index 8bd84b0a..de1644aa 100644 --- a/install.php +++ b/install.php @@ -61,7 +61,7 @@ if(!defined('JSON_PRETTY_PRINT')) { define('JSON', function_exists('json_encode')); // Database format date -define('DB_DATE_FORMAT', 'Y-m-d H:i'); +define('DB_DATE_FORMAT', 'Y-m-d H:i:s'); // Charset, default UTF-8. define('CHARSET', 'UTF-8'); diff --git a/kernel/abstract/content.class.php b/kernel/abstract/content.class.php new file mode 100644 index 00000000..710605f5 --- /dev/null +++ b/kernel/abstract/content.class.php @@ -0,0 +1,274 @@ +build($path)===false) { + $this->vars = false; + } + } + + // Return true if valid + public function isValid() + { + return($this->vars!==false); + } + + public function getField($field) + { + if(isset($this->vars[$field])) { + return $this->vars[$field]; + } + + return false; + } + + // $notoverwrite true if you don't want to replace the value if are set previusly + public function setField($field, $value, $overwrite=true) + { + if($overwrite || empty($this->vars[$field])) { + $this->vars[$field] = $value; + } + + return true; + } + + private function build($path) + { + if( !Sanitize::pathFile($path, 'index.txt') ) { + return false; + } + + $tmp = 0; + $lines = file($path.'index.txt'); + foreach($lines as $lineNumber=>$line) + { + $parts = array_map('trim', explode(':', $line, 2)); + + // Lowercase variable + $parts[0] = Text::lowercase($parts[0]); + + // If variables is content then break the foreach and process the content after. + if($parts[0]==='content') + { + $tmp = $lineNumber; + break; + } + + if( !empty($parts[0]) && !empty($parts[1]) ) { + // Sanitize all fields, except Content. + $this->vars[$parts[0]] = Sanitize::html($parts[1]); + } + } + + // Process the content. + if($tmp!==0) + { + // Next line after "Content:" variable + $tmp++; + + // Remove lines after Content + $output = array_slice($lines, $tmp); + + if(!empty($parts[1])) { + array_unshift($output, "\n"); + array_unshift($output, $parts[1]); + } + + $implode = implode($output); + $this->vars['content'] = $implode; + + // Sanitize content. + //$this->vars['content'] = Sanitize::html($implode); + } + + } + + // Returns the post title. + public function title() + { + return $this->getField('title'); + } + + // Returns the content. + // This content is markdown parser. + // (boolean) $fullContent, TRUE returns all content, if FALSE returns the first part of the content. + // (boolean) $raw, TRUE returns the content without sanitized, FALSE otherwise. + public function content($fullContent=true, $raw=true) + { + // This content is not sanitized. + $content = $this->getField('content'); + + if(!$fullContent) { + $content = $this->getField('breakContent'); + } + + if($raw) { + return $content; + } + + return Sanitize::html($content); + } + + public function readMore() + { + return $this->getField('readMore'); + } + + // Returns the content. This content is not markdown parser. + // (boolean) $raw, TRUE returns the content without sanitized, FALSE otherwise. + public function contentRaw($raw=true) + { + // This content is not sanitized. + $content = $this->getField('contentRaw'); + + if($raw) { + return $content; + } + + return Sanitize::html($content); + } + + public function key() + { + return $this->getField('key'); + } + + // Returns TRUE if the post is published, FALSE otherwise. + public function published() + { + return ($this->getField('status')==='published'); + } + + // Returns TRUE if the post is scheduled, FALSE otherwise. + public function scheduled() + { + return ($this->getField('status')==='scheduled'); + } + + // Returns TRUE if the post is draft, FALSE otherwise. + public function draft() + { + return ($this->getField('status')=='draft'); + } + + public function coverImage($absolute=true) + { + $fileName = $this->getField('coverImage'); + + if(empty($fileName)) { + return false; + } + + if($absolute) { + return HTML_PATH_UPLOADS.$fileName; + } + + return $fileName; + } + + public function profilePicture() + { + return HTML_PATH_UPLOADS_PROFILES.$this->username().'.jpg'; + } + + // Returns the user object if $field is false, otherwise returns the field's value. + public function user($field=false) + { + // Get the user object. + $User = $this->getField('user'); + + if($field) { + return $User->getField($field); + } + + return $User; + } + + public function username() + { + return $this->getField('username'); + } + + public function description() + { + return $this->getField('description'); + } + + // Returns the post date according to locale settings and format settings. + public function date() + { + return $this->getField('date'); + } + + // Returns the post date according to locale settings and format as database stored. + public function dateRaw($format=false) + { + $date = $this->getField('dateRaw'); + + if($format) { + return Date::format($date, DB_DATE_FORMAT, $format); + } + + return $date; + } + + public function tags($returnsArray=false) + { + global $Url; + + $tags = $this->getField('tags'); + + if($returnsArray) { + + if($tags==false) { + return array(); + } + + return $tags; + } + else { + if($tags==false) { + return false; + } + + // Return string with tags separeted by comma. + return implode(', ', $tags); + } + } + + public function permalink($absolute=false) + { + global $Url; + global $Site; + + $filterType = $this->getField('filterType'); + + $url = trim(DOMAIN_BASE,'/'); + $key = $this->key(); + $filter = trim($Url->filters($filterType), '/'); + $htmlPath = trim(HTML_PATH_ROOT,'/'); + + if(empty($filter)) { + $tmp = $key; + } + else { + $tmp = $filter.'/'.$key; + } + + if($absolute) { + return $url.'/'.$tmp; + } + + if(empty($htmlPath)) { + return '/'.$tmp; + } + + return '/'.$htmlPath.'/'.$tmp; + } + + +} diff --git a/kernel/abstract/filecontent.class.php b/kernel/abstract/filecontent.class.php deleted file mode 100644 index ffd5b80e..00000000 --- a/kernel/abstract/filecontent.class.php +++ /dev/null @@ -1,90 +0,0 @@ -build($path)===false) { - $this->vars = false; - } - } - - // Return true if valid - public function isValid() - { - return($this->vars!==false); - } - - public function getField($field) - { - if(isset($this->vars[$field])) { - return $this->vars[$field]; - } - - return false; - } - - // $notoverwrite true if you don't want to replace the value if are set previusly - public function setField($field, $value, $overwrite=true) - { - if($overwrite || empty($this->vars[$field])) { - $this->vars[$field] = $value; - } - - return true; - } - - private function build($path) - { - if( !Sanitize::pathFile($path, 'index.txt') ) { - return false; - } - - $tmp = 0; - $lines = file($path.'index.txt'); - foreach($lines as $lineNumber=>$line) - { - $parts = array_map('trim', explode(':', $line, 2)); - - // Lowercase variable - $parts[0] = Text::lowercase($parts[0]); - - // If variables is content then break the foreach and process the content after. - if($parts[0]==='content') - { - $tmp = $lineNumber; - break; - } - - if( !empty($parts[0]) && !empty($parts[1]) ) { - // Sanitize all fields, except Content. - $this->vars[$parts[0]] = Sanitize::html($parts[1]); - } - } - - // Process the content. - if($tmp!==0) - { - // Next line after "Content:" variable - $tmp++; - - // Remove lines after Content - $output = array_slice($lines, $tmp); - - if(!empty($parts[1])) { - array_unshift($output, "\n"); - array_unshift($output, $parts[1]); - } - - $implode = implode($output); - $this->vars['content'] = $implode; - - // Sanitize content. - //$this->vars['content'] = Sanitize::html($implode); - } - - } - -} diff --git a/kernel/abstract/plugin.class.php b/kernel/abstract/plugin.class.php index 49284713..5ca8e245 100644 --- a/kernel/abstract/plugin.class.php +++ b/kernel/abstract/plugin.class.php @@ -68,6 +68,11 @@ class Plugin { return PATH_PLUGINS.$this->directoryName.DS; } + public function phpPathDB() + { + return PATH_PLUGINS_DATABASES.$this->directoryName.DS; + } + // Returns the item from plugin-data. public function getData($key) { @@ -181,7 +186,13 @@ class Plugin { public function uninstall() { - unlink($this->filenameDb); + // Delete all files. + $files = Filesystem::listFiles( $this->phpPathDB() ); + foreach($files as $file) { + unlink($file); + } + + // Delete the directory. rmdir(PATH_PLUGINS_DATABASES.$this->directoryName); } diff --git a/kernel/admin/controllers/edit-page.php b/kernel/admin/controllers/edit-page.php index 504517bc..594b418c 100644 --- a/kernel/admin/controllers/edit-page.php +++ b/kernel/admin/controllers/edit-page.php @@ -1,5 +1,9 @@ edit($args) ) + // Add the page, if the $key is FALSE the creation of the post failure. + $key = $dbPages->edit($args); + + if($key) { $dbPages->regenerateCli(); + // Call the plugins after page created. + Theme::plugins('afterPageModify'); + + // Alert the user Alert::set($Language->g('The changes have been saved')); Redirect::page('admin', 'edit-page/'.$args['slug']); } @@ -34,6 +44,9 @@ function deletePage($key) if( $dbPages->delete($key) ) { + // Call the plugins after post created. + Theme::plugins('afterPageDelete'); + Alert::set($Language->g('The page has been deleted successfully')); Redirect::page('admin', 'manage-pages'); } diff --git a/kernel/admin/controllers/edit-post.php b/kernel/admin/controllers/edit-post.php index 8bfb274b..d4f4b9b3 100644 --- a/kernel/admin/controllers/edit-post.php +++ b/kernel/admin/controllers/edit-post.php @@ -13,12 +13,18 @@ function editPost($args) global $dbPosts; global $Language; - // Edit the post. - if( $dbPosts->edit($args) ) + // Add the page, if the $key is FALSE the creation of the post failure. + $key = $dbPosts->edit($args); + + if($key) { // Reindex tags, this function is in 70.posts.php reIndexTagsPosts(); + // Call the plugins after post created. + Theme::plugins('afterPostModify'); + + // Alert the user Alert::set($Language->g('The changes have been saved')); Redirect::page('admin', 'edit-post/'.$args['slug']); } @@ -40,6 +46,9 @@ function deletePost($key) // Reindex tags, this function is in 70.posts.php reIndexTagsPosts(); + // Call the plugins after post created. + Theme::plugins('afterPostDelete'); + Alert::set($Language->g('The post has been deleted successfully')); Redirect::page('admin', 'manage-posts'); } diff --git a/kernel/admin/controllers/new-page.php b/kernel/admin/controllers/new-page.php index c7540305..5846f975 100644 --- a/kernel/admin/controllers/new-page.php +++ b/kernel/admin/controllers/new-page.php @@ -13,9 +13,15 @@ function addPage($args) global $dbPages; global $Language; - // Add the page. - if( $dbPages->add($args) ) + // Add the page, if the $key is FALSE the creation of the post failure. + $key = $dbPages->add($args); + + if($key) { + // Call the plugins after page created. + Theme::plugins('afterPageCreate'); + + // Alert the user Alert::set($Language->g('Page added successfully')); Redirect::page('admin', 'manage-pages'); } diff --git a/kernel/admin/controllers/new-post.php b/kernel/admin/controllers/new-post.php index 48e40f4e..53b19667 100644 --- a/kernel/admin/controllers/new-post.php +++ b/kernel/admin/controllers/new-post.php @@ -13,12 +13,18 @@ function addPost($args) global $dbPosts; global $Language; - // Add the page. - if( $dbPosts->add($args) ) + // Add the page, if the $key is FALSE the creation of the post failure. + $key = $dbPosts->add($args); + + if($key) { // Reindex tags, this function is in 70.posts.php reIndexTagsPosts(); + // Call the plugins after post created. + Theme::plugins('afterPostCreate'); + + // Alert for the user Alert::set($Language->g('Post added successfully')); Redirect::page('admin', 'manage-posts'); } diff --git a/kernel/admin/themes/default/css/jquery.auto-complete.css b/kernel/admin/themes/default/css/jquery.auto-complete.css new file mode 100755 index 00000000..4261b1d0 --- /dev/null +++ b/kernel/admin/themes/default/css/jquery.auto-complete.css @@ -0,0 +1,9 @@ +.autocomplete-suggestions { + text-align: left; cursor: default; border: 1px solid #ccc; border-top: 0; background: #fff; box-shadow: -1px 1px 3px rgba(0,0,0,.1); + + /* core styles should not be changed */ + position: absolute; display: none; z-index: 9999; max-height: 254px; overflow: hidden; overflow-y: auto; box-sizing: border-box; +} +.autocomplete-suggestion { position: relative; padding: 0 .6em; line-height: 23px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.02em; color: #333; } +.autocomplete-suggestion b { font-weight: normal; color: #1f8dd6; } +.autocomplete-suggestion.selected { background: #f0f0f0; } diff --git a/kernel/admin/themes/default/index.php b/kernel/admin/themes/default/index.php index 94432b29..d90c144e 100644 --- a/kernel/admin/themes/default/index.php +++ b/kernel/admin/themes/default/index.php @@ -20,12 +20,14 @@ + + diff --git a/kernel/admin/themes/default/init.php b/kernel/admin/themes/default/init.php index f2fc324f..00e44f33 100644 --- a/kernel/admin/themes/default/init.php +++ b/kernel/admin/themes/default/init.php @@ -52,6 +52,28 @@ class HTML { echo $html; } + public static function formInputAutocomplete($args) + { + self::formInputText($args); + +$script = ''; + + echo $script; + + } + public static function formInputPassword($args) { $args['type'] = 'password'; @@ -130,6 +152,7 @@ class HTML { public static function bluditQuickImages() { + global $L; $html = ''; $html .= ' @@ -153,7 +176,7 @@ if(empty($thumbnailList)) { } $html .= ' -More images +'.$L->g('More images').' '; @@ -327,7 +350,7 @@ if(empty($thumbnailList)) { $html .= ' diff --git a/kernel/admin/themes/default/js/jquery.auto-complete.min.js b/kernel/admin/themes/default/js/jquery.auto-complete.min.js new file mode 100755 index 00000000..6fdb3bc6 --- /dev/null +++ b/kernel/admin/themes/default/js/jquery.auto-complete.min.js @@ -0,0 +1,3 @@ +// jQuery autoComplete v1.0.7 +// https://github.com/Pixabay/jQuery-autoComplete +!function(e){e.fn.autoComplete=function(t){var o=e.extend({},e.fn.autoComplete.defaults,t);return"string"==typeof t?(this.each(function(){var o=e(this);"destroy"==t&&(e(window).off("resize.autocomplete",o.updateSC),o.off("blur.autocomplete focus.autocomplete keydown.autocomplete keyup.autocomplete"),o.data("autocomplete")?o.attr("autocomplete",o.data("autocomplete")):o.removeAttr("autocomplete"),e(o.data("sc")).remove(),o.removeData("sc").removeData("autocomplete"))}),this):this.each(function(){function t(e){var t=s.val();if(s.cache[t]=e,e.length&&t.length>=o.minChars){for(var a="",c=0;c'),s.data("sc",s.sc).data("autocomplete",s.attr("autocomplete")),s.attr("autocomplete","off"),s.cache={},s.last_val="",s.updateSC=function(t,o){if(s.sc.css({top:s.offset().top+s.outerHeight(),left:s.offset().left,width:s.outerWidth()}),!t&&(s.sc.show(),s.sc.maxHeight||(s.sc.maxHeight=parseInt(s.sc.css("max-height"))),s.sc.suggestionHeight||(s.sc.suggestionHeight=e(".autocomplete-suggestion",s.sc).first().outerHeight()),s.sc.suggestionHeight))if(o){var a=s.sc.scrollTop(),c=o.offset().top-s.sc.offset().top;c+s.sc.suggestionHeight-s.sc.maxHeight>0?s.sc.scrollTop(c+s.sc.suggestionHeight+a-s.sc.maxHeight):0>c&&s.sc.scrollTop(c+a)}else s.sc.scrollTop(0)},e(window).on("resize.autocomplete",s.updateSC),s.sc.appendTo("body"),s.sc.on("mouseleave",".autocomplete-suggestion",function(){e(".autocomplete-suggestion.selected").removeClass("selected")}),s.sc.on("mouseenter",".autocomplete-suggestion",function(){e(".autocomplete-suggestion.selected").removeClass("selected"),e(this).addClass("selected")}),s.sc.on("mousedown",".autocomplete-suggestion",function(t){var a=e(this),c=a.data("val");return(c||a.hasClass("autocomplete-suggestion"))&&(s.val(c),o.onSelect(t,c,a),s.sc.hide()),!1}),s.on("blur.autocomplete",function(){try{over_sb=e(".autocomplete-suggestions:hover").length}catch(t){over_sb=0}over_sb?s.is(":focus")||setTimeout(function(){s.focus()},20):(s.last_val=s.val(),s.sc.hide(),setTimeout(function(){s.sc.hide()},350))}),o.minChars||s.on("focus.autocomplete",function(){s.last_val="\n",s.trigger("keyup.autocomplete")}),s.on("keydown.autocomplete",function(t){if((40==t.which||38==t.which)&&s.sc.html()){var a,c=e(".autocomplete-suggestion.selected",s.sc);return c.length?(a=40==t.which?c.next(".autocomplete-suggestion"):c.prev(".autocomplete-suggestion"),a.length?(c.removeClass("selected"),s.val(a.addClass("selected").data("val"))):(c.removeClass("selected"),s.val(s.last_val),a=0)):(a=40==t.which?e(".autocomplete-suggestion",s.sc).first():e(".autocomplete-suggestion",s.sc).last(),s.val(a.addClass("selected").data("val"))),s.updateSC(0,a),!1}if(27==t.which)s.val(s.last_val).sc.hide();else if(13==t.which||9==t.which){var c=e(".autocomplete-suggestion.selected",s.sc);c.length&&s.sc.is(":visible")&&(o.onSelect(t,c.data("val"),c),setTimeout(function(){s.sc.hide()},20))}}),s.on("keyup.autocomplete",function(a){if(!~e.inArray(a.which,[13,27,35,36,37,38,39,40])){var c=s.val();if(c.length>=o.minChars){if(c!=s.last_val){if(s.last_val=c,clearTimeout(s.timer),o.cache){if(c in s.cache)return void t(s.cache[c]);for(var l=1;l'+e.replace(o,"$1")+""},onSelect:function(e,t,o){}}}(jQuery); \ No newline at end of file diff --git a/kernel/admin/views/edit-page.php b/kernel/admin/views/edit-page.php index 62393751..abac66b7 100644 --- a/kernel/admin/views/edit-page.php +++ b/kernel/admin/views/edit-page.php @@ -77,12 +77,13 @@ echo '