aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNicolas Lœuillet <nicolas.loeuillet@gmail.com>2013-12-12 01:48:24 -0800
committerNicolas Lœuillet <nicolas.loeuillet@gmail.com>2013-12-12 01:48:24 -0800
commit05d6dd487ceaf8c00510ebe5e0e762fcc11df691 (patch)
treeebe6a344aa054a37c1c2d2e54efc59a67930e873
parentd460914f65254d201911a8346792d680218c8dc3 (diff)
parent6bf4702608e4f32d66a9840ec93461f653315a76 (diff)
downloadwallabag-05d6dd487ceaf8c00510ebe5e0e762fcc11df691.tar.gz
wallabag-05d6dd487ceaf8c00510ebe5e0e762fcc11df691.tar.zst
wallabag-05d6dd487ceaf8c00510ebe5e0e762fcc11df691.zip
Merge pull request #356 from inthepoche/tags
Tags feature
-rw-r--r--inc/poche/Database.class.php71
-rw-r--r--inc/poche/Poche.class.php79
-rw-r--r--inc/poche/Tools.class.php41
-rw-r--r--index.php3
-rw-r--r--install/mysql.sql15
-rwxr-xr-xinstall/poche.sqlitebin360448 -> 393216 bytes
-rw-r--r--install/postgres.sql13
-rw-r--r--themes/default/_menu.twig1
-rw-r--r--themes/default/css/style.css6
-rw-r--r--themes/default/edit-tags.twig20
-rw-r--r--themes/default/img/default/rss.pngbin0 -> 288 bytes
-rw-r--r--themes/default/tag.twig33
-rw-r--r--themes/default/tags.twig8
-rw-r--r--themes/default/view.twig3
14 files changed, 252 insertions, 41 deletions
diff --git a/inc/poche/Database.class.php b/inc/poche/Database.class.php
index c233eda1..d95b9b81 100644
--- a/inc/poche/Database.class.php
+++ b/inc/poche/Database.class.php
@@ -249,4 +249,75 @@ class Database {
249 public function getLastId($column = '') { 249 public function getLastId($column = '') {
250 return $this->getHandle()->lastInsertId($column); 250 return $this->getHandle()->lastInsertId($column);
251 } 251 }
252
253 public function retrieveAllTags() {
254 $sql = "SELECT * FROM tags";
255 $query = $this->executeQuery($sql, array());
256 $tags = $query->fetchAll();
257
258 return $tags;
259 }
260
261 public function retrieveTag($id) {
262 $tag = NULL;
263 $sql = "SELECT * FROM tags WHERE id=?";
264 $params = array(intval($id));
265 $query = $this->executeQuery($sql, $params);
266 $tag = $query->fetchAll();
267
268 return isset($tag[0]) ? $tag[0] : null;
269 }
270
271 public function retrieveEntriesByTag($tag_id) {
272 $sql =
273 "SELECT * FROM entries
274 LEFT JOIN tags_entries ON tags_entries.entry_id=entries.id
275 WHERE tags_entries.tag_id = ?";
276 $query = $this->executeQuery($sql, array($tag_id));
277 $entries = $query->fetchAll();
278
279 return $entries;
280 }
281
282 public function retrieveTagsByEntry($entry_id) {
283 $sql =
284 "SELECT * FROM tags
285 LEFT JOIN tags_entries ON tags_entries.tag_id=tags.id
286 WHERE tags_entries.entry_id = ?";
287 $query = $this->executeQuery($sql, array($entry_id));
288 $tags = $query->fetchAll();
289
290 return $tags;
291 }
292
293 public function removeTagForEntry($entry_id, $tag_id) {
294 $sql_action = "DELETE FROM tags_entries WHERE tag_id=? AND entry_id=?";
295 $params_action = array($tag_id, $entry_id);
296 $query = $this->executeQuery($sql_action, $params_action);
297 return $query;
298 }
299
300 public function retrieveTagByValue($value) {
301 $tag = NULL;
302 $sql = "SELECT * FROM tags WHERE value=?";
303 $params = array($value);
304 $query = $this->executeQuery($sql, $params);
305 $tag = $query->fetchAll();
306
307 return isset($tag[0]) ? $tag[0] : null;
308 }
309
310 public function createTag($value) {
311 $sql_action = 'INSERT INTO tags ( value ) VALUES (?)';
312 $params_action = array($value);
313 $query = $this->executeQuery($sql_action, $params_action);
314 return $query;
315 }
316
317 public function setTagToEntry($tag_id, $entry_id) {
318 $sql_action = 'INSERT INTO tags_entries ( tag_id, entry_id ) VALUES (?, ?)';
319 $params_action = array($tag_id, $entry_id);
320 $query = $this->executeQuery($sql_action, $params_action);
321 return $query;
322 }
252} 323}
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php
index d45d0c40..d415dd03 100644
--- a/inc/poche/Poche.class.php
+++ b/inc/poche/Poche.class.php
@@ -397,6 +397,36 @@ class Poche
397 Tools::redirect(); 397 Tools::redirect();
398 } 398 }
399 break; 399 break;
400 case 'add_tag' :
401 $tags = explode(',', $_POST['value']);
402 $entry_id = $_POST['entry_id'];
403 foreach($tags as $key => $tag_value) {
404 $value = trim($tag_value);
405 $tag = $this->store->retrieveTagByValue($value);
406
407 if (is_null($tag)) {
408 # we create the tag
409 $tag = $this->store->createTag($value);
410 $sequence = '';
411 if (STORAGE == 'postgres') {
412 $sequence = 'tags_id_seq';
413 }
414 $tag_id = $this->store->getLastId($sequence);
415 }
416 else {
417 $tag_id = $tag['id'];
418 }
419
420 # we assign the tag to the article
421 $this->store->setTagToEntry($tag_id, $entry_id);
422 }
423 Tools::redirect();
424 break;
425 case 'remove_tag' :
426 $tag_id = $_GET['tag_id'];
427 $this->store->removeTagForEntry($id, $tag_id);
428 Tools::redirect();
429 break;
400 default: 430 default:
401 break; 431 break;
402 } 432 }
@@ -430,6 +460,31 @@ class Poche
430 ); 460 );
431 Tools::logm('config view'); 461 Tools::logm('config view');
432 break; 462 break;
463 case 'edit-tags':
464 # tags
465 $tags = $this->store->retrieveTagsByEntry($id);
466 $tpl_vars = array(
467 'entry_id' => $id,
468 'tags' => $tags,
469 );
470 break;
471 case 'tag':
472 $entries = $this->store->retrieveEntriesByTag($id);
473 $tag = $this->store->retrieveTag($id);
474 $tpl_vars = array(
475 'tag' => $tag,
476 'entries' => $entries,
477 );
478 break;
479 case 'tags':
480 $token = $this->user->getConfigValue('token');
481 $tags = $this->store->retrieveAllTags();
482 $tpl_vars = array(
483 'token' => $token,
484 'user_id' => $this->user->getId(),
485 'tags' => $tags,
486 );
487 break;
433 case 'view': 488 case 'view':
434 $entry = $this->store->retrieveOneById($id, $this->user->getId()); 489 $entry = $this->store->retrieveOneById($id, $this->user->getId());
435 if ($entry != NULL) { 490 if ($entry != NULL) {
@@ -443,12 +498,16 @@ class Poche
443 498
444 # flattr checking 499 # flattr checking
445 $flattr = new FlattrItem(); 500 $flattr = new FlattrItem();
446 $flattr->checkItem($entry['url'],$entry['id']); 501 $flattr->checkItem($entry['url'], $entry['id']);
502
503 # tags
504 $tags = $this->store->retrieveTagsByEntry($entry['id']);
447 505
448 $tpl_vars = array( 506 $tpl_vars = array(
449 'entry' => $entry, 507 'entry' => $entry,
450 'content' => $content, 508 'content' => $content,
451 'flattr' => $flattr 509 'flattr' => $flattr,
510 'tags' => $tags
452 ); 511 );
453 } 512 }
454 else { 513 else {
@@ -859,9 +918,9 @@ class Poche
859 $_SESSION['poche_user']->setConfig($currentConfig); 918 $_SESSION['poche_user']->setConfig($currentConfig);
860 } 919 }
861 920
862 public function generateFeeds($token, $user_id, $type = 'home') 921 public function generateFeeds($token, $user_id, $tag_id, $type = 'home')
863 { 922 {
864 $allowed_types = array('home', 'fav', 'archive'); 923 $allowed_types = array('home', 'fav', 'archive', 'tag');
865 $config = $this->store->getConfigUser($user_id); 924 $config = $this->store->getConfigUser($user_id);
866 925
867 if (!in_array($type, $allowed_types) || 926 if (!in_array($type, $allowed_types) ||
@@ -876,7 +935,13 @@ class Poche
876 $feed->setChannelElement('updated', date(DATE_RSS , time())); 935 $feed->setChannelElement('updated', date(DATE_RSS , time()));
877 $feed->setChannelElement('author', 'poche'); 936 $feed->setChannelElement('author', 'poche');
878 937
879 $entries = $this->store->getEntriesByView($type, $user_id); 938 if ($type == 'tag') {
939 $entries = $this->store->retrieveEntriesByTag($tag_id);
940 }
941 else {
942 $entries = $this->store->getEntriesByView($type, $user_id);
943 }
944
880 if (count($entries) > 0) { 945 if (count($entries) > 0) {
881 foreach ($entries as $entry) { 946 foreach ($entries as $entry) {
882 $newItem = $feed->createNewItem(); 947 $newItem = $feed->createNewItem();
diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php
index 9d8e1fd6..63916582 100644
--- a/inc/poche/Tools.class.php
+++ b/inc/poche/Tools.class.php
@@ -88,39 +88,16 @@ class Tools
88 88
89 public static function getTplFile($view) 89 public static function getTplFile($view)
90 { 90 {
91 $default_tpl = 'home.twig'; 91 $views = array(
92 92 'install', 'import', 'export', 'config', 'tags',
93 switch ($view) { 93 'edit-tags', 'view', 'login', 'error', 'tag'
94 case 'install': 94 );
95 $tpl_file = 'install.twig'; 95
96 break; 96 if (in_array($view, $views)) {
97 case 'import'; 97 return $view . '.twig';
98 $tpl_file = 'import.twig';
99 break;
100 case 'export':
101 $tpl_file = 'export.twig';
102 break;
103 case 'config':
104 $tpl_file = 'config.twig';
105 break;
106 case 'view':
107 $tpl_file = 'view.twig';
108 break;
109
110 case 'login':
111 $tpl_file = 'login.twig';
112 break;
113
114 case 'error':
115 $tpl_file = 'error.twig';
116 break;
117
118 default:
119 $tpl_file = $default_tpl;
120 break;
121 } 98 }
122 99
123 return $tpl_file; 100 return 'home.twig';
124 } 101 }
125 102
126 public static function getFile($url) 103 public static function getFile($url)
diff --git a/index.php b/index.php
index d2b363b0..145da772 100644
--- a/index.php
+++ b/index.php
@@ -77,7 +77,8 @@ if (isset($_GET['login'])) {
77 $poche->generateToken(); 77 $poche->generateToken();
78 } 78 }
79 else { 79 else {
80 $poche->generateFeeds($_GET['token'], $_GET['user_id'], $_GET['type']); 80 $tag_id = (isset($_GET['tag_id']) ? intval($_GET['tag_id']) : 0);
81 $poche->generateFeeds($_GET['token'], $_GET['user_id'], $tag_id, $_GET['type']);
81 } 82 }
82} 83}
83 84
diff --git a/install/mysql.sql b/install/mysql.sql
index 9b01e32c..66c4bb31 100644
--- a/install/mysql.sql
+++ b/install/mysql.sql
@@ -31,4 +31,19 @@ CREATE TABLE IF NOT EXISTS `users_config` (
31 `name` varchar(255) NOT NULL, 31 `name` varchar(255) NOT NULL,
32 `value` varchar(255) NOT NULL, 32 `value` varchar(255) NOT NULL,
33 PRIMARY KEY (`id`) 33 PRIMARY KEY (`id`)
34) ENGINE=InnoDB DEFAULT CHARSET=utf8;
35
36CREATE TABLE tags (
37 `id` int(11) NOT NULL AUTO_INCREMENT,
38 `value` varchar(255) NOT NULL,
39 PRIMARY KEY (`id`)
40) ENGINE=InnoDB DEFAULT CHARSET=utf8;
41
42CREATE TABLE tags_entries (
43 `id` int(11) NOT NULL AUTO_INCREMENT,
44 `entry_id` int(11) NOT NULL,
45 `tag_id` int(11) NOT NULL,
46 FOREIGN KEY(entry_id) REFERENCES entries(id) ON DELETE CASCADE,
47 FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE,
48 PRIMARY KEY (`id`)
34) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file 49) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file
diff --git a/install/poche.sqlite b/install/poche.sqlite
index 7abf1f62..b911f272 100755
--- a/install/poche.sqlite
+++ b/install/poche.sqlite
Binary files differ
diff --git a/install/postgres.sql b/install/postgres.sql
index 9e0e8276..fe8f559c 100644
--- a/install/postgres.sql
+++ b/install/postgres.sql
@@ -27,4 +27,15 @@ CREATE TABLE users_config (
27 user_id integer NOT NULL, 27 user_id integer NOT NULL,
28 name varchar(255) NOT NULL, 28 name varchar(255) NOT NULL,
29 value varchar(255) NOT NULL 29 value varchar(255) NOT NULL
30); \ No newline at end of file 30);
31
32CREATE TABLE tags (
33 id bigserial primary key,
34 value varchar(255) NOT NULL
35);
36
37CREATE TABLE tags_entries (
38 id bigserial primary key,
39 entry_id integer NOT NULL,
40 tag_id integer NOT NULL
41) \ No newline at end of file
diff --git a/themes/default/_menu.twig b/themes/default/_menu.twig
index 699d6a0c..02bec1dc 100644
--- a/themes/default/_menu.twig
+++ b/themes/default/_menu.twig
@@ -2,6 +2,7 @@
2 <li><a href="./" {% if view == 'home' %}class="current"{% endif %}>{% trans "home" %}</a></li> 2 <li><a href="./" {% if view == 'home' %}class="current"{% endif %}>{% trans "home" %}</a></li>
3 <li><a href="./?view=fav" {% if view == 'fav' %}class="current"{% endif %}>{% trans "favorites" %}</a></li> 3 <li><a href="./?view=fav" {% if view == 'fav' %}class="current"{% endif %}>{% trans "favorites" %}</a></li>
4 <li><a href="./?view=archive" {% if view == 'archive' %}class="current"{% endif %}>{% trans "archive" %}</a></li> 4 <li><a href="./?view=archive" {% if view == 'archive' %}class="current"{% endif %}>{% trans "archive" %}</a></li>
5 <li><a href="./?view=tags" {% if view == 'tags' %}class="current"{% endif %}>{% trans "tags" %}</a></li>
5 <li><a href="./?view=config" {% if view == 'config' %}class="current"{% endif %}>{% trans "config" %}</a></li> 6 <li><a href="./?view=config" {% if view == 'config' %}class="current"{% endif %}>{% trans "config" %}</a></li>
6 <li><a href="./?logout" title="{% trans "logout" %}">{% trans "logout" %}</a></li> 7 <li><a href="./?logout" title="{% trans "logout" %}">{% trans "logout" %}</a></li>
7 </ul> \ No newline at end of file 8 </ul> \ No newline at end of file
diff --git a/themes/default/css/style.css b/themes/default/css/style.css
index 670eb50f..2088ee2e 100644
--- a/themes/default/css/style.css
+++ b/themes/default/css/style.css
@@ -176,6 +176,12 @@ a:visited {
176 text-decoration: none; 176 text-decoration: none;
177} 177}
178 178
179#article .tags {
180 font-size: 0.8em;
181 color: #888;
182 padding-bottom: 5px;
183}
184
179.backhome { 185.backhome {
180 display: inline; 186 display: inline;
181} 187}
diff --git a/themes/default/edit-tags.twig b/themes/default/edit-tags.twig
new file mode 100644
index 00000000..7116bba9
--- /dev/null
+++ b/themes/default/edit-tags.twig
@@ -0,0 +1,20 @@
1{% extends "layout.twig" %}
2{% block title %}edit tags{% endblock %}
3{% block menu %}
4{% include '_menu.twig' %}
5{% endblock %}
6{% block content %}
7{% if tags is empty %}
8no tags
9{% endif %}
10<ul>
11{% for tag in tags %}<li>{{ tag.value }} <a href="./?action=remove_tag&amp;tag_id={{ tag.id }}&amp;id={{ entry_id }}">✘</a></li>{% endfor %}
12</ul>
13<form method="post" action="./?action=add_tag">
14 <label for="value">New tags: </label><input type="text" id="value" name="value" required="required" />
15 <p>{% trans "you can type several tags, separated by comma" %}</p>
16 <input type="hidden" name="entry_id" value="{{ entry_id }}" />
17 <input type="submit" value="add tags" />
18</form>
19<a href="./?view=view&id={{ entry_id }}">{% trans "back to the article" %}</a>
20{% endblock %} \ No newline at end of file
diff --git a/themes/default/img/default/rss.png b/themes/default/img/default/rss.png
new file mode 100644
index 00000000..21bad1a1
--- /dev/null
+++ b/themes/default/img/default/rss.png
Binary files differ
diff --git a/themes/default/tag.twig b/themes/default/tag.twig
new file mode 100644
index 00000000..364c7cd4
--- /dev/null
+++ b/themes/default/tag.twig
@@ -0,0 +1,33 @@
1{% extends "layout.twig" %}
2{% block title %}tag {% endblock %}
3{% block menu %}
4{% include '_menu.twig' %}
5{% endblock %}
6{% block content %}
7 <h3>{% trans "Tag" %} {{ tag.value }}</h3>
8 {% if entries is empty %}
9 <div class="messages warning"><p>{% trans "No link available here!" %}</p></div>
10 {% else %}
11 {% block pager %}
12 {% if nb_results > 1 %}
13 <div class="results">
14 <div class="nb-results">{{ nb_results }} {% trans "results" %}</div>
15 {{ page_links | raw }}
16 </div>
17 {% endif %}
18 {% endblock %}
19 {% for entry in entries %}
20 <div id="entry-{{ entry.id|e }}" class="entrie">
21 <h2><a href="index.php?view=view&amp;id={{ entry.id|e }}">{{ entry.title|raw }}</a></h2>
22 <ul class="tools">
23 <li><a title="{% trans "toggle mark as read" %}" class="tool {% if entry.is_read == 0 %}archive-off{% else %}archive{% endif %}" href="./?action=toggle_archive&amp;id={{ entry.id|e }}"><span>{% trans "toggle mark as read" %}</span></a></li>
24 <li><a title="{% trans "toggle favorite" %}" class="tool {% if entry.is_fav == 0 %}fav-off{% else %}fav{% endif %}" href="./?action=toggle_fav&amp;id={{ entry.id|e }}"><span>{% trans "toggle favorite" %}</span></a></li>
25 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&amp;id={{ entry.id|e }}"><span>{% trans "delete" %}</span></a></li>
26 <li><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}" class="tool link"><span>{{ entry.url | e | getDomain }}</span></a></li>
27 <li><a target="_blank" title="{% trans "estimated reading time:" %} {{ entry.content| getReadingTime }} min" class="reading-time"><span>{{ entry.content| getReadingTime }} min</span></a></li>
28 </ul>
29 <p>{{ entry.content|striptags|slice(0, 300) }}...</p>
30 </div>
31 {% endfor %}
32 {% endif %}
33{% endblock %} \ No newline at end of file
diff --git a/themes/default/tags.twig b/themes/default/tags.twig
new file mode 100644
index 00000000..cff6b1d7
--- /dev/null
+++ b/themes/default/tags.twig
@@ -0,0 +1,8 @@
1{% extends "layout.twig" %}
2{% block title %}tags{% endblock %}
3{% block menu %}
4{% include '_menu.twig' %}
5{% endblock %}
6{% block content %}
7{% for tag in tags %}<a href="./?view=tag&amp;id={{ tag.id }}">{{ tag.value }}</a> {% if token != '' %}<a href="?feed&amp;type=tag&amp;user_id={{ user_id }}&amp;tag_id={{ tag.id }}&amp;token={{ token }}" target="_blank"><img src="{{ poche_url }}/themes/{{ theme }}/img/{{ theme }}/rss.png" /></a>{% endif %} {% endfor %}
8{% endblock %} \ No newline at end of file
diff --git a/themes/default/view.twig b/themes/default/view.twig
index 1e54ae38..64672b61 100644
--- a/themes/default/view.twig
+++ b/themes/default/view.twig
@@ -20,6 +20,9 @@
20 <header class="mbm"> 20 <header class="mbm">
21 <h1>{{ entry.title|raw }}</h1> 21 <h1>{{ entry.title|raw }}</h1>
22 </header> 22 </header>
23 <aside class="tags">
24 tags: {% for tag in tags %}<a href="./?view=tag&amp;id={{ tag.id }}">{{ tag.value }}</a> {% endfor %}<a href="./?view=edit-tags&amp;id={{ entry.id|e }}" title="{% trans "edit tags" %}">✎</a>
25 </aside>
23 <article> 26 <article>
24 {{ content | raw }} 27 {{ content | raw }}
25 </article> 28 </article>