diff --git a/cache/installedVersion.json b/cache/installedVersion.json
new file mode 100644
index 0000000..2d86412
--- /dev/null
+++ b/cache/installedVersion.json
@@ -0,0 +1,3 @@
+{
+ "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/functions.php b/system/includes/functions.php
index 1644e78..0af8f2b 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 'UpdateMe[' . $updater->getName() . ']';
+ }
echo 'Logout';
echo '';
diff --git a/system/includes/updater.php b/system/includes/updater.php
new file mode 100644
index 0000000..d0db34f
--- /dev/null
+++ b/system/includes/updater.php
@@ -0,0 +1,161 @@
+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 = [];
+
+ 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
+ {
+ $fileContent = @file_get_contents($path,false, stream_context_create(['http'=>['header'=>"User-Agent: Awesome-Update-My-Self\r\n"]]));
+ if($fileContent == false)
+ {
+ return [];
+ }
+ $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(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([
+ "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 = $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);
+ if($zip->statIndex($i)["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