diff --git a/check_setup.php b/check_setup.php new file mode 100644 index 0000000..96dd0f7 --- /dev/null +++ b/check_setup.php @@ -0,0 +1,40 @@ + "short_open_tag = On"'); + } +} + +// Check PDO Sqlite +if (! extension_loaded('pdo_sqlite')) { + die('PHP extension required: pdo_sqlite'); +} + +// Check ZIP +if (! extension_loaded('zip')) { + die('PHP extension required: zip'); +} + +// Check if /cache is writeable +if (! is_writable('cache')) { + die('The directory "cache" must be writeable by your web server user'); +} + +// Check if /db is writeable +if (! is_writable('db')) { + die('The directory "db" must be writeable by your web server user'); +} + +// install folder still present, need to install wallabag +if (is_dir('install')) { + require('install/index.php'); + exit; +} \ No newline at end of file diff --git a/inc/poche/Database.class.php b/inc/poche/Database.class.php index 9f553fa..3332b5a 100644 --- a/inc/poche/Database.class.php +++ b/inc/poche/Database.class.php @@ -241,6 +241,22 @@ class Database { return isset($entry[0]) ? $entry[0] : null; } + public function retrieveOneByURL($url, $user_id) { + $entry = NULL; + $sql = "SELECT * FROM entries WHERE url=? AND user_id=?"; + $params = array($url, $user_id); + $query = $this->executeQuery($sql, $params); + $entry = $query->fetchAll(); + + return isset($entry[0]) ? $entry[0] : null; + } + + public function reassignTags($old_entry_id, $new_entry_id) { + $sql = "UPDATE tags_entries SET entry_id=? WHERE entry_id=?"; + $params = array($new_entry_id, $old_entry_id); + $query = $this->executeQuery($sql, $params); + } + public function getEntriesByView($view, $user_id, $limit = '') { switch ($_SESSION['sort']) { @@ -328,30 +344,36 @@ class Database { return $this->getHandle()->lastInsertId($column); } - public function retrieveAllTags() { - $sql = "SELECT * FROM tags"; - $query = $this->executeQuery($sql, array()); + public function retrieveAllTags($user_id) { + $sql = "SELECT tags.* FROM tags + LEFT JOIN tags_entries ON tags_entries.tag_id=tags.id + LEFT JOIN entries ON tags_entries.entry_id=entries.id + WHERE entries.user_id=?"; + $query = $this->executeQuery($sql, array($user_id)); $tags = $query->fetchAll(); return $tags; } - public function retrieveTag($id) { + public function retrieveTag($id, $user_id) { $tag = NULL; - $sql = "SELECT * FROM tags WHERE id=?"; - $params = array(intval($id)); + $sql = "SELECT tags.* FROM tags + LEFT JOIN tags_entries ON tags_entries.tag_id=tags.id + LEFT JOIN entries ON tags_entries.entry_id=entries.id + WHERE tags.id=? AND entries.user_id=?"; + $params = array(intval($id), $user_id); $query = $this->executeQuery($sql, $params); $tag = $query->fetchAll(); return isset($tag[0]) ? $tag[0] : null; } - public function retrieveEntriesByTag($tag_id) { + public function retrieveEntriesByTag($tag_id, $user_id) { $sql = "SELECT entries.* FROM entries LEFT JOIN tags_entries ON tags_entries.entry_id=entries.id - WHERE tags_entries.tag_id = ?"; - $query = $this->executeQuery($sql, array($tag_id)); + WHERE tags_entries.tag_id = ? AND entries.user_id=?"; + $query = $this->executeQuery($sql, array($tag_id, $user_id)); $entries = $query->fetchAll(); return $entries; diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php index 5eba356..753bd7f 100644 --- a/inc/poche/Poche.class.php +++ b/inc/poche/Poche.class.php @@ -374,6 +374,11 @@ class Poche $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled'); $body = $content['rss']['channel']['item']['description']; + //search for possible duplicate if not in import mode + if (!$import) { + $duplicate = $this->store->retrieveOneByURL($url->getUrl(), $this->user->getId()); + } + if ($this->store->add($url->getUrl(), $title, $body, $this->user->getId())) { Tools::logm('add link ' . $url->getUrl()); $sequence = ''; @@ -386,6 +391,20 @@ class Poche Tools::logm('updating content article'); $this->store->updateContent($last_id, $content, $this->user->getId()); } + + if ($duplicate != NULL) { + // duplicate exists, so, older entry needs to be deleted (as new entry should go to the top of list), BUT favorite mark and tags should be preserved + Tools::logm('link ' . $url->getUrl() . ' is a duplicate'); + // 1) - preserve tags and favorite, then drop old entry + $this->store->reassignTags($duplicate['id'], $last_id); + if ($duplicate['is_fav']) { + $this->store->favoriteById($last_id, $this->user->getId()); + } + if ($this->store->deleteById($duplicate['id'], $this->user->getId())) { + Tools::logm('previous link ' . $url->getUrl() .' entry deleted'); + } + } + if (!$import) { $this->messages->add('s', _('the link has been added successfully')); } @@ -444,6 +463,12 @@ class Poche case 'add_tag' : $tags = explode(',', $_POST['value']); $entry_id = $_POST['entry_id']; + $entry = $this->store->retrieveOneById($entry_id, $this->user->getId()); + if (!$entry) { + $this->messages->add('e', _('Article not found!')); + Tools::logm('error : article not found'); + Tools::redirect(); + } foreach($tags as $key => $tag_value) { $value = trim($tag_value); $tag = $this->store->retrieveTagByValue($value); @@ -468,6 +493,12 @@ class Poche break; case 'remove_tag' : $tag_id = $_GET['tag_id']; + $entry = $this->store->retrieveOneById($id, $this->user->getId()); + if (!$entry) { + $this->messages->add('e', _('Article not found!')); + Tools::logm('error : article not found'); + Tools::redirect(); + } $this->store->removeTagForEntry($id, $tag_id); Tools::redirect(); break; @@ -506,6 +537,12 @@ class Poche break; case 'edit-tags': # tags + $entry = $this->store->retrieveOneById($id, $this->user->getId()); + if (!$entry) { + $this->messages->add('e', _('Article not found!')); + Tools::logm('error : article not found'); + Tools::redirect(); + } $tags = $this->store->retrieveTagsByEntry($id); $tpl_vars = array( 'entry_id' => $id, @@ -513,8 +550,8 @@ class Poche ); break; case 'tag': - $entries = $this->store->retrieveEntriesByTag($id); - $tag = $this->store->retrieveTag($id); + $entries = $this->store->retrieveEntriesByTag($id, $this->user->getId()); + $tag = $this->store->retrieveTag($id, $this->user->getId()); $tpl_vars = array( 'tag' => $tag, 'entries' => $entries, @@ -522,7 +559,7 @@ class Poche break; case 'tags': $token = $this->user->getConfigValue('token'); - $tags = $this->store->retrieveAllTags(); + $tags = $this->store->retrieveAllTags($this->user->getId()); $tpl_vars = array( 'token' => $token, 'user_id' => $this->user->getId(), @@ -1037,7 +1074,7 @@ class Poche $feed->setChannelElement('author', 'wallabag'); if ($type == 'tag') { - $entries = $this->store->retrieveEntriesByTag($tag_id); + $entries = $this->store->retrieveEntriesByTag($tag_id, $user_id); } else { $entries = $this->store->getEntriesByView($type, $user_id); @@ -1057,4 +1094,20 @@ class Poche $feed->genarateFeed(); exit; } + + public function emptyCache() { + $files = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator(CACHE, RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($files as $fileinfo) { + $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink'); + $todo($fileinfo->getRealPath()); + } + + Tools::logm('empty cache'); + $this->messages->add('s', _('Cache deleted.')); + Tools::redirect(); + } } diff --git a/index.php b/index.php index 60755cf..7ded3bd 100644 --- a/index.php +++ b/index.php @@ -9,6 +9,7 @@ */ define ('POCHE', '1.5.0'); +require 'check_setup.php'; require_once 'inc/poche/global.inc.php'; session_start(); @@ -65,7 +66,9 @@ if (isset($_GET['login'])) { } elseif (isset($_GET['import'])) { $import = $poche->import($_GET['from']); } elseif (isset($_GET['download'])) { - Tools::download_db();; + Tools::download_db(); +} elseif (isset($_GET['empty-cache'])) { + $poche->emptyCache(); } elseif (isset($_GET['export'])) { $poche->export(); } elseif (isset($_GET['updatetheme'])) { diff --git a/install/index.php b/install/index.php new file mode 100644 index 0000000..d213717 --- /dev/null +++ b/install/index.php @@ -0,0 +1,283 @@ +download it here manually<∕a> and unzip it in your wallabag folder.'; + } + else { + if (extension_loaded('zip')) { + $zip = new ZipArchive(); + if ($zip->open("cache/vendor.zip") !== TRUE){ + $errors[] = 'Impossible to open cache/vendor.zip. Please unzip it manually in your wallabag folder.'; + } + if ($zip->extractTo(realpath(''))) { + @unlink("cache/vendor.zip"); + $successes[] = 'twig is now installed, you can install wallabag.'; + } + else { + $errors[] = 'Impossible to extract cache/vendor.zip. Please unzip it manually in your wallabag folder.'; + } + $zip->close(); + } + else { + $errors[] = 'zip extension is not enabled in your PHP configuration. Please unzip cache/vendor.zip in your wallabag folder.'; + } + } +} +else if ($_POST['install']) { + if (!is_dir('vendor')) { + $errors[] = 'You must install twig before.'; + } + else { + $continue = true; + // Create config.inc.php + if (!copy('inc/poche/config.inc.php.new', 'inc/poche/config.inc.php')) { + $errors[] = 'Installation aborted, impossible to create inc/poche/config.inc.php file. Maybe you don\'t have write access to create it.'; + $continue = false; + } + else { + function generate_salt() { + mt_srand(microtime(true)*100000 + memory_get_usage(true)); + return md5(uniqid(mt_rand(), true)); + } + + $content = file_get_contents('inc/poche/config.inc.php'); + $salt = generate_salt(); + $content = str_replace("define ('SALT', '');", "define ('SALT', '".$salt."');", $content); + file_put_contents('inc/poche/config.inc.php', $content); + } + + if ($continue) { + + // User informations + $username = trim($_POST['username']); + $password = trim($_POST['password']); + $salted_password = sha1($password . $username . $salt); + + // Database informations + if ($_POST['db_engine'] == 'sqlite') { + if (!copy('install/poche.sqlite', 'db/poche.sqlite')) { + $errors[] = 'Impossible to create inc/poche/config.inc.php file.'; + $continue = false; + } + else { + $db_path = 'sqlite:' . realpath('') . '/db/poche.sqlite'; + $handle = new PDO($db_path); + } + } + else { + $content = file_get_contents('inc/poche/config.inc.php'); + + if ($_POST['db_engine'] == 'mysql') { + $db_path = 'mysql:host=' . $_POST['mysql_server'] . ';dbname=' . $_POST['mysql_database']; + $content = str_replace("define ('STORAGE_SERVER', 'localhost');", "define ('STORAGE_SERVER', '".$_POST['mysql_server']."');", $content); + $content = str_replace("define ('STORAGE_DB', 'poche');", "define ('STORAGE_DB', '".$_POST['mysql_database']."');", $content); + $content = str_replace("define ('STORAGE_USER', 'poche');", "define ('STORAGE_USER', '".$_POST['mysql_user']."');", $content); + $content = str_replace("define ('STORAGE_PASSWORD', 'poche');", "define ('STORAGE_PASSWORD', '".$_POST['mysql_password']."');", $content); + $handle = new PDO($db_path, $_POST['mysql_user'], $_POST['mysql_password']); + + $sql_structure = file_get_contents('install/mysql.sql'); + } + else if ($_POST['db_engine'] == 'postgresql') { + $db_path = 'pgsql:host=' . $_POST['pg_server'] . ';dbname=' . $_POST['pg_database']; + $content = str_replace("define ('STORAGE_SERVER', 'localhost');", "define ('STORAGE_SERVER', '".$_POST['pg_server']."');", $content); + $content = str_replace("define ('STORAGE_DB', 'poche');", "define ('STORAGE_DB', '".$_POST['pg_database']."');", $content); + $content = str_replace("define ('STORAGE_USER', 'poche');", "define ('STORAGE_USER', '".$_POST['pg_user']."');", $content); + $content = str_replace("define ('STORAGE_PASSWORD', 'poche');", "define ('STORAGE_PASSWORD', '".$_POST['pg_password']."');", $content); + $handle = new PDO($db_path, $_POST['pg_user'], $_POST['pg_password']); + + $sql_structure = file_get_contents('install/postgres.sql'); + } + + $content = str_replace("define ('STORAGE', 'sqlite');", "define ('STORAGE', '".$_POST['db_engine']."');", $content); + file_put_contents('inc/poche/config.inc.php', $content); + } + + if ($continue) { + + function executeQuery($handle, $sql, $params) { + try + { + $query = $handle->prepare($sql); + $query->execute($params); + return $query->fetchAll(); + } + catch (Exception $e) + { + return FALSE; + } + } + + // create database structure + $query = executeQuery($handle, $sql_structure, array()); + + // Create user + $handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $sql = 'INSERT INTO users (username, password, name) VALUES (?, ?, ?)'; + $params = array($username, $salted_password, $username); + $query = executeQuery($handle, $sql, $params); + + $id_user = $handle->lastInsertId(); + + $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; + $params = array($id_user, 'pager', '10'); + $query = executeQuery($handle, $sql, $params); + + $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; + $params = array($id_user, 'language', 'en_EN.UTF8'); + $query = executeQuery($handle, $sql, $params); + + $successes[] = 'wallabag is now installed. Don\'t forget to delete install folder.'; + } + } + } +} +?> + + + + + + + wallabag — installation + + + + + + + + + + + + + + +
+

+ logo poche +

+
+
+ + + +
+

Errors during installation:

+

+

+

+

Please reload this page when you think you resolved these problems.

+
+ + +
+

+

+

+
+ +

To install wallabag, you just have to fill the following fields. That's all.

+

Don't forget to check your server compatibility here.

+
+
+ Technical settings + +
wallabag needs twig, a template engine (?). Two ways to install it: +
    +
  • automatically download and extract vendor.zip into your wallabag folder. +

    + + Be careful, zip extension is not enabled in your PHP configuration. You'll have to unzip vendor.zip manually. + + This method is mainly recommended if you don't have a dedicated server.
  • +
  • use Composer :
    curl -s http://getcomposer.org/installer | php
    +php composer.phar install
  • +
+
+ +

+ Database engine: +

    +
  • +
  • + +
      +
    • +
    • +
    • +
    • +
    +
  • +
  • + +
      +
    • +
    • +
    • + id
    • +
    +
  • +
+

+
+ +
+ User settings +

+ + +

+

+ + +

+

+ +

+
+ + +
+
+ + + \ No newline at end of file diff --git a/themes/baggy/config.twig b/themes/baggy/config.twig index d0ec0ca..598409b 100644 --- a/themes/baggy/config.twig +++ b/themes/baggy/config.twig @@ -116,4 +116,8 @@ {% if constant('STORAGE') == 'sqlite' %}

{% trans "Click here" %} {% trans "to download your database." %}

{% endif %}

{% trans "Click here" %} {% trans "to export your wallabag data." %}

+ +

{% trans "Cache" %}

+

{% trans "Click here" %} {% trans "to delete cache." %}

+ {% endblock %} diff --git a/themes/baggy/css/messages.css b/themes/baggy/css/messages.css index e69de29..0cd89a9 100755 --- a/themes/baggy/css/messages.css +++ b/themes/baggy/css/messages.css @@ -0,0 +1,19 @@ +.messages.error { + border: 1px solid #c42608; + color: #c00 !important; + background: #fff0ef; + text-align: left; +} + +.messages.notice { + border: 1px solid #ebcd41; + color: #000; + background: #fffcd3; + text-align: left; +} + +.messages.success { + border: 1px solid #6dc70c; + background: #e0fbcc; + text-align: left; +} \ No newline at end of file diff --git a/themes/baggy/home.twig b/themes/baggy/home.twig index d58e53c..33afdbb 100644 --- a/themes/baggy/home.twig +++ b/themes/baggy/home.twig @@ -43,7 +43,7 @@ {% endfor %} - {% if view == 'home' %}{% if nb_results > 1 %}{{ "Mark all the entries as read" }} {% trans "" %}{% endif %}{% endif %} + {% if view == 'home' %}{% if nb_results > 1 %}{{ "Mark all the entries as read" }}{% endif %}{% endif %} {% endif %} {{ block('pager') }} {% endblock %} diff --git a/wallabag_compatibility_test.php b/wallabag_compatibility_test.php index fd28504..26dce01 100644 --- a/wallabag_compatibility_test.php +++ b/wallabag_compatibility_test.php @@ -176,7 +176,7 @@ div.chunk { PHP - 5.2.0 or higher + 5.3.3 or higher