diff --git a/cache/installedVersion.json b/cache/installedVersion.json new file mode 100644 index 0000000..54985e8 --- /dev/null +++ b/cache/installedVersion.json @@ -0,0 +1,4 @@ +{ + "id": 397789, + "tag_name": "v2.1" +} \ No newline at end of file diff --git a/system/admin/views/updated-to.html.php b/system/admin/views/updated-to.html.php new file mode 100644 index 0000000..c11dc91 --- /dev/null +++ b/system/admin/views/updated-to.html.php @@ -0,0 +1,5 @@ +printOne(); + +?> \ No newline at end of file diff --git a/system/htmly.php b/system/htmly.php index 9720f68..0c29618 100644 --- a/system/htmly.php +++ b/system/htmly.php @@ -6,6 +6,7 @@ date_default_timezone_set('Asia/Jakarta'); // Explicitly including the dispatch framework, // and our functions.php file require 'system/includes/dispatch.php'; +require 'system/includes/updater.php'; require 'system/includes/functions.php'; require 'system/admin/admin.php'; require 'system/includes/session.php'; @@ -1109,6 +1110,26 @@ get('/feed/opml',function(){ }); +get('/admin/update/now/:csrf',function($CSRF){ + + $proper = is_csrf_proper($CSRF); + $updater = new Updater; + if( login() && $proper && $updater->updateAble()) + { + $updater->update(); + config('views.root', 'system/admin/views'); + render('updated-to', array( + 'head_contents' => head_contents('Updated - ' . blog_title(), blog_description(), site_url()), + 'updater' => $updater, + )); + } + else + { + $login = site_url() . 'login'; + header("location: $login"); + } +}); + // If we get here, it means that // nothing has been matched above diff --git a/system/includes/dispatch.php b/system/includes/dispatch.php index 81df1d4..9b269c5 100644 --- a/system/includes/dispatch.php +++ b/system/includes/dispatch.php @@ -272,53 +272,51 @@ function content($value = null) { } function render($view, $locals = null, $layout = null) { + $login = login(); + if(!$login) { + $c = str_replace('/', '#', str_replace('?', '~', $_SERVER['REQUEST_URI'])); + $dir = 'cache/page'; + $cachefile = $dir. '/' . $c . '.cache'; + if(is_dir($dir) === false) { + mkdir($dir, 0777, true); + } + } - if(!login()) { - $c = str_replace('/', '#', str_replace('?', '~', $_SERVER['REQUEST_URI'])); - $dir = 'cache/page'; - $cachefile = $dir. '/' . $c . '.cache'; - if(is_dir($dir) === false) { - mkdir($dir, 0777, true); - } - } + if (is_array($locals) && count($locals)) { + extract($locals, EXTR_SKIP); + } - if (is_array($locals) && count($locals)) { - extract($locals, EXTR_SKIP); - } + if (($view_root = config('views.root')) == null) + error(500, "[views.root] is not set"); - if (($view_root = config('views.root')) == null) - error(500, "[views.root] is not set"); + ob_start(); + include "{$view_root}/{$view}.html.php"; + content(trim(ob_get_clean())); - ob_start(); - include "{$view_root}/{$view}.html.php"; - content(trim(ob_get_clean())); + if ($layout !== false) { - if ($layout !== false) { + if ($layout == null) { + $layout = config('views.layout'); + $layout = ($layout == null) ? 'layout' : $layout; + } - if ($layout == null) { - $layout = config('views.layout'); - $layout = ($layout == null) ? 'layout' : $layout; - } + $layout = "{$view_root}/{$layout}.html.php"; - $layout = "{$view_root}/{$layout}.html.php"; + header('Content-type: text/html; charset=utf-8'); - header('Content-type: text/html; charset=utf-8'); + ob_start(); + require $layout; - ob_start(); - require $layout; - - if(!login()) { - if (!file_exists($cachefile)) { - file_put_contents($cachefile, ob_get_contents()); - } + if(!$login) { + if (!file_exists($cachefile)) { + file_put_contents($cachefile, ob_get_contents()); + } } - echo trim(ob_get_clean()); - - } else { - echo content(); - } - + echo trim(ob_get_clean()); + } else { + echo content(); + } } function json($obj, $code = 200) { diff --git a/system/includes/functions.php b/system/includes/functions.php index 1644e78..23f9aad 100644 --- a/system/includes/functions.php +++ b/system/includes/functions.php @@ -1673,6 +1673,10 @@ function toolbar() { $role = user('role', $user); $base = site_url(); + $CSRF = get_csrf(); + + $updater = new Updater; + echo << EOF; @@ -1686,6 +1690,10 @@ EOF; echo '
  • Import
  • '; echo '
  • Backup
  • '; echo '
  • Clear cache
  • '; + if( $updater->updateAble()) + { + echo '
  • Update to ' . $updater->getName() . '
  • '; + } echo '
  • Logout
  • '; echo ''; diff --git a/system/includes/updater.php b/system/includes/updater.php new file mode 100644 index 0000000..e00762f --- /dev/null +++ b/system/includes/updater.php @@ -0,0 +1,175 @@ +fileName = $fileName; + $this->holdTime = $holdTime; + } + + public function is() + { + if(! file_exists($this->fileName)) + return false; + if(filemtime($this->fileName) < ( time() - $this->holdTime ) ) + { + unlink($this->fileName); + return false; + } + return true; + } + public function get() + { + return file_get_contents($this->fileName); + } + public function set($content) + { + file_put_contents($this->fileName,$content); + } +} + +class Updater +{ + protected $cachedInfo = "cache/downloadInfo.json"; + protected $versionFile = "cache/installedVersion.json"; + protected $zipFile = "cache/tmpZipFile.zip"; + + protected $infos = array(); + + public function __construct() + { + if(! file_exists("cache/")) + { + mkdir("cache/"); + } + $this->cachedInfo = new CacheOneFile($this->cachedInfo); + $this->infos = $this->getInfos(); + } + + protected function getInfos() + { + $path = "https://api.github.com/repos/danpros/htmly/releases"; + if($this->cachedInfo->is()) + { + $fileContent = $this->cachedInfo->get(); + } + else + { + if(!in_array('https', stream_get_wrappers())) + { + return array(); + } + $fileContent = @file_get_contents($path,false, stream_context_create( + array( + 'http' => array( + 'header'=>"User-Agent: Awesome-Update-My-Self\r\n" + ) + ) + )); + if($fileContent === false) + { + return array(); + } + $json = json_decode($fileContent,true); + $fileContent = json_encode($json, JSON_PRETTY_PRINT); + $this->cachedInfo->set($fileContent); + return $json; + } + return json_decode($fileContent,true); + } + + public function updateAble() + { + if(!in_array('https', stream_get_wrappers())) + return false; + if(empty($this->infos)) + return false; + + if(file_exists($this->versionFile)) + { + $fileContent = file_get_contents($this->versionFile); + $current = json_decode($fileContent,true); + + if(isset($current['id']) && $current['id'] == $this->infos[0]['id']) + return false; + if(isset($current['tag_name']) && $current['tag_name'] == $this->infos[0]['tag_name']) + return false; + } + return true; + } + + public function update() + { + if($this->updateAble()) + { + if($this->download("https://github.com/danpros/htmly/archive/" . $this->infos[0]['tag_name'] . ".zip")) + { + if($this->unZip()) + { + unlink($this->zipFile); + file_put_contents($this->versionFile, json_encode(array( + "id" => $this->infos[0]['id'], + "tag_name" => $this->infos[0]['tag_name'] + ), JSON_PRETTY_PRINT)); + return true; + } + } + } + return false; + } + protected function download($url) + { + $file = @fopen($url, 'r'); + if($file == false) + return false; + file_put_contents($this->zipFile, $file); + return true; + } + protected function unZip() + { + $path = dirname($_SERVER['SCRIPT_FILENAME']) . "/" . $this->zipFile; + + $zip = new ZipArchive; + if ($zip->open($path) === true) { + $cutLength = strlen($zip->getNameIndex(0)); + for($i = 1; $i < $zip->numFiles; $i++) {//iterate throw the Zip + $fileName = $zip->getNameIndex($i); + $stat = $zip->statIndex($i); + if($stat["crc"] == 0) + { + $dirName = substr($fileName,$cutLength); + if(! file_exists($dirName)) + { + mkdir($dirName); + } + } + else{ + copy("zip://".$path."#".$fileName, substr($fileName,$cutLength)); + } + } + $zip->close(); + return true; + } + else{ + return false; + } + } + + public function printOne() + { + $releases = $this->infos; + $string = "

    Updated to

    "; + $string .= "

    [" . $releases[0]['tag_name'] . "] " . $releases[0]['name'] . "

    \n"; + $string .= "

    " . $releases[0]['body'] . "

    \n"; + return $string; + } + + public function getName() + { + return $this->infos[0]['tag_name']; + } +} \ No newline at end of file