aboutsummaryrefslogtreecommitdiffhomepage
path: root/tpl
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2017-03-25 15:59:01 +0100
committerArthurHoaro <arthur@hoa.ro>2017-05-25 15:25:04 +0200
commitaa4797ba3679b847adc895e2f817ac058779a171 (patch)
treed854a1ab8748911dd10e8bced31a2d9b80ccf57b /tpl
parentbc988eb0420156219fdeb7af684fff37c8b33f4b (diff)
downloadShaarli-aa4797ba3679b847adc895e2f817ac058779a171.tar.gz
Shaarli-aa4797ba3679b847adc895e2f817ac058779a171.tar.zst
Shaarli-aa4797ba3679b847adc895e2f817ac058779a171.zip
Adds a taglist view with edit/delete buttons
* The tag list can be sort alphabetically or by most used tag * Edit/Delete are perform using AJAX, or fallback to 'do=changetag' page * New features aren't backported to vintage theme
Diffstat (limited to 'tpl')
-rw-r--r--tpl/default/changetag.html4
-rw-r--r--tpl/default/css/shaarli.css50
-rw-r--r--tpl/default/js/shaarli.js136
-rw-r--r--tpl/default/tag.list.html82
-rw-r--r--tpl/default/tag.sort.html8
5 files changed, 276 insertions, 4 deletions
diff --git a/tpl/default/changetag.html b/tpl/default/changetag.html
index 8d263a16..49dd20d9 100644
--- a/tpl/default/changetag.html
+++ b/tpl/default/changetag.html
@@ -11,7 +11,7 @@
11 <h2 class="window-title">{"Manage tags"|t}</h2> 11 <h2 class="window-title">{"Manage tags"|t}</h2>
12 <form method="POST" action="#" name="changetag" id="changetag"> 12 <form method="POST" action="#" name="changetag" id="changetag">
13 <div> 13 <div>
14 <input type="text" name="fromtag" placeholder="{'Tag'|t}" 14 <input type="text" name="fromtag" placeholder="{'Tag'|t}" value="{$fromtag}"
15 list="tagsList" autocomplete="off" class="awesomplete autofocus" data-minChars="1"> 15 list="tagsList" autocomplete="off" class="awesomplete autofocus" data-minChars="1">
16 <datalist id="tagsList"> 16 <datalist id="tagsList">
17 {loop="$tags"}<option>{$key}</option>{/loop} 17 {loop="$tags"}<option>{$key}</option>{/loop}
@@ -31,6 +31,8 @@
31 <input type="submit" value="{'Delete'|t}" name="deletetag" class="button button-red confirm-delete"> 31 <input type="submit" value="{'Delete'|t}" name="deletetag" class="button button-red confirm-delete">
32 </div> 32 </div>
33 </form> 33 </form>
34
35 <p>You can also edit tags in the <a href="?do=taglist&sort=usage">tag list</a>.</p>
34 </div> 36 </div>
35</div> 37</div>
36{include="page.footer"} 38{include="page.footer"}
diff --git a/tpl/default/css/shaarli.css b/tpl/default/css/shaarli.css
index 4415a1b7..2eda5df4 100644
--- a/tpl/default/css/shaarli.css
+++ b/tpl/default/css/shaarli.css
@@ -751,10 +751,11 @@ body, .pure-g [class*="pure-u"] {
751.page-form a { 751.page-form a {
752 color: #1b926c; 752 color: #1b926c;
753 font-weight: bold; 753 font-weight: bold;
754 text-decoration: none;
754} 755}
755 756
756.page-form p { 757.page-form p {
757 padding: 0 10px; 758 padding: 5px 10px;
758 margin: 0; 759 margin: 0;
759} 760}
760 761
@@ -1070,7 +1071,7 @@ form[name="linkform"].page-form {
1070} 1071}
1071 1072
1072#cloudtag, #cloudtag a { 1073#cloudtag, #cloudtag a {
1073 color: #000; 1074 color: #252525;
1074 text-decoration: none; 1075 text-decoration: none;
1075} 1076}
1076 1077
@@ -1079,6 +1080,38 @@ form[name="linkform"].page-form {
1079} 1080}
1080 1081
1081/** 1082/**
1083 * TAG LIST
1084 */
1085#taglist {
1086 padding: 0 10px;
1087}
1088
1089#taglist a {
1090 color: #252525;
1091 text-decoration: none;
1092}
1093
1094#taglist .count {
1095 display: inline-block;
1096 width: 35px;
1097 text-align: right;
1098 color: #7f7f7f;
1099}
1100
1101#taglist .delete-tag {
1102 color: #ac2925;
1103 display: none;
1104}
1105
1106#taglist .rename-tag {
1107 color: #0b5ea6;
1108}
1109
1110#taglist .validate-rename-tag {
1111 color: #1b926c;
1112}
1113
1114/**
1082 * Picture wall CSS 1115 * Picture wall CSS
1083 */ 1116 */
1084#picwall_container { 1117#picwall_container {
@@ -1227,3 +1260,16 @@ form[name="linkform"].page-form {
1227.pure-button { 1260.pure-button {
1228 -moz-user-select: auto; 1261 -moz-user-select: auto;
1229} 1262}
1263
1264.tag-sort {
1265 margin-top: 30px;
1266 text-align: center;
1267}
1268
1269.tag-sort a {
1270 display: inline-block;
1271 margin: 0 15px;
1272 color: white;
1273 text-decoration: none;
1274 font-weight: bold;
1275}
diff --git a/tpl/default/js/shaarli.js b/tpl/default/js/shaarli.js
index ceb1d1b8..e19e9001 100644
--- a/tpl/default/js/shaarli.js
+++ b/tpl/default/js/shaarli.js
@@ -412,8 +412,139 @@ window.onload = function () {
412 } 412 }
413 }); 413 });
414 } 414 }
415
416 /**
417 * Tag list operations
418 *
419 * TODO: support error code in the backend for AJAX requests
420 */
421 // Display/Hide rename form
422 var renameTagButtons = document.querySelectorAll('.rename-tag');
423 [].forEach.call(renameTagButtons, function(rename) {
424 rename.addEventListener('click', function(event) {
425 event.preventDefault();
426 var block = findParent(event.target, 'div', {'class': 'tag-list-item'});
427 var form = block.querySelector('.rename-tag-form');
428 form.style.display = form.style.display == 'none' ? 'block' : 'none';
429 });
430 });
431
432 // Rename a tag with an AJAX request
433 var renameTagSubmits = document.querySelectorAll('.validate-rename-tag');
434 [].forEach.call(renameTagSubmits, function(rename) {
435 rename.addEventListener('click', function(event) {
436 event.preventDefault();
437 var block = findParent(event.target, 'div', {'class': 'tag-list-item'});
438 var input = block.querySelector('.rename-tag-input');
439 var totag = input.value.replace('/"/g', '\\"');
440 if (totag.trim() == '') {
441 return;
442 }
443 var fromtag = block.getAttribute('data-tag');
444 var token = document.getElementById('token').value;
445
446 xhr = new XMLHttpRequest();
447 xhr.open('POST', '?do=changetag');
448 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
449 xhr.onload = function() {
450 if (xhr.status !== 200) {
451 alert('An error occurred. Return code: '+ xhr.status);
452 location.reload();
453 } else {
454 block.setAttribute('data-tag', totag);
455 input.setAttribute('name', totag);
456 input.setAttribute('value', totag);
457 input.parentNode.style.display = 'none';
458 block.querySelector('a.tag-link').innerHTML = htmlEntities(totag);
459 block.querySelector('a.tag-link').setAttribute('href', '?searchtags='+ encodeURIComponent(totag));
460 block.querySelector('a.rename-tag').setAttribute('href', '?do=changetag&fromtag='+ encodeURIComponent(totag));
461 }
462 };
463 xhr.send('renametag=1&fromtag='+ encodeURIComponent(fromtag) +'&totag='+ encodeURIComponent(totag) +'&token='+ token);
464 refreshToken();
465 });
466 });
467
468 // Validate input with enter key
469 var renameTagInputs = document.querySelectorAll('.rename-tag-input');
470 [].forEach.call(renameTagInputs, function(rename) {
471 rename.addEventListener('keypress', function(event) {
472 if (event.keyCode === 13) { // enter
473 findParent(event.target, 'div', {'class': 'tag-list-item'}).querySelector('.validate-rename-tag').click();
474 }
475 });
476 });
477
478 // Delete a tag with an AJAX query (alert popup confirmation)
479 var deleteTagButtons = document.querySelectorAll('.delete-tag');
480 [].forEach.call(deleteTagButtons, function(rename) {
481 rename.style.display = 'inline';
482 rename.addEventListener('click', function(event) {
483 event.preventDefault();
484 var block = findParent(event.target, 'div', {'class': 'tag-list-item'});
485 var tag = block.getAttribute('data-tag');
486 var token = document.getElementById('token').value;
487
488 if (confirm('Are you sure you want to delete the tag "'+ tag +'"?')) {
489 xhr = new XMLHttpRequest();
490 xhr.open('POST', '?do=changetag');
491 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
492 xhr.onload = function() {
493 block.remove();
494 };
495 xhr.send(encodeURI('deletetag=1&fromtag='+ tag +'&token='+ token));
496 refreshToken();
497 }
498 });
499 });
415}; 500};
416 501
502function findParent(element, tagName, attributes)
503{
504 while (element) {
505 if (element.tagName.toLowerCase() == tagName) {
506 var match = true;
507 for (var key in attributes) {
508 if (! element.hasAttribute(key)
509 || (attributes[key] != '' && element.getAttribute(key).indexOf(attributes[key]) == -1)
510 ) {
511 match = false;
512 break;
513 }
514 }
515
516 if (match) {
517 return element;
518 }
519 }
520 element = element.parentElement;
521 }
522 return null;
523}
524
525function refreshToken()
526{
527 var xhr = new XMLHttpRequest();
528 xhr.open('GET', '?do=token');
529 xhr.onload = function() {
530 var token = document.getElementById('token');
531 token.setAttribute('value', xhr.responseText);
532 };
533 xhr.send();
534}
535
536/**
537 * html_entities in JS
538 *
539 * @see http://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript
540 */
541function htmlEntities(str)
542{
543 return str.replace(/[\u00A0-\u9999<>\&]/gim, function(i) {
544 return '&#'+i.charCodeAt(0)+';';
545 });
546}
547
417function activateFirefoxSocial(node) { 548function activateFirefoxSocial(node) {
418 var loc = location.href; 549 var loc = location.href;
419 var baseURL = loc.substring(0, loc.lastIndexOf("/")); 550 var baseURL = loc.substring(0, loc.lastIndexOf("/"));
@@ -445,8 +576,11 @@ function activateFirefoxSocial(node) {
445 * @param currentContinent Current selected continent 576 * @param currentContinent Current selected continent
446 * @param reset Set to true to reset the selected value 577 * @param reset Set to true to reset the selected value
447 */ 578 */
448function hideTimezoneCities(cities, currentContinent, reset = false) { 579function hideTimezoneCities(cities, currentContinent) {
449 var first = true; 580 var first = true;
581 if (reset == null) {
582 reset = false;
583 }
450 [].forEach.call(cities, function (option) { 584 [].forEach.call(cities, function (option) {
451 if (option.getAttribute('data-continent') != currentContinent) { 585 if (option.getAttribute('data-continent') != currentContinent) {
452 option.className = 'hidden'; 586 option.className = 'hidden';
diff --git a/tpl/default/tag.list.html b/tpl/default/tag.list.html
new file mode 100644
index 00000000..98971051
--- /dev/null
+++ b/tpl/default/tag.list.html
@@ -0,0 +1,82 @@
1<!DOCTYPE html>
2<html>
3<head>
4 {include="includes"}
5</head>
6<body>
7{include="page.header"}
8
9{include="tag.sort"}
10
11<div class="pure-g">
12 <div class="pure-u-lg-1-6 pure-u-1-24"></div>
13 <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor">
14 {$countTags=count($tags)}
15 <h2 class="window-title">{'Tag list'|t} - {$countTags} {'tags'|t}</h2>
16
17 <div id="search-tagcloud" class="pure-g">
18 <div class="pure-u-lg-1-4"></div>
19 <div class="pure-u-1 pure-u-lg-1-2">
20 <form method="GET">
21 <input type="hidden" name="do" value="taglist">
22 <input type="text" name="searchtags" placeholder="{'Filter by tag'|t}"
23 {if="!empty($search_tags)"}
24 value="{$search_tags}"
25 {/if}
26 autocomplete="off" data-multiple data-autofirst data-minChars="1"
27 data-list="{loop="$tags"}{$key}, {/loop}"
28 >
29 <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
30 </form>
31 </div>
32 <div class="pure-u-lg-1-4"></div>
33 </div>
34
35 <div id="plugin_zone_start_tagcloud" class="plugin_zone">
36 {loop="$plugin_start_zone"}
37 {$value}
38 {/loop}
39 </div>
40
41 <div id="taglist">
42 {loop="tags"}
43 <div class="tag-list-item pure-g" data-tag="{$key}">
44 <div class="pure-u-1">
45 {if="isLoggedIn()===true"}
46 <a href="#" class="delete-tag"><i class="fa fa-trash"></i></a>&nbsp;&nbsp;
47 <a href="?do=changetag&fromtag={$key|urlencode}" class="rename-tag">
48 <i class="fa fa-pencil-square-o {$key}"></i>
49 </a>
50 {/if}
51
52 <a href="?addtag={$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value}</a>
53 <a href="?searchtags={$key|urlencode}" class="tag-link">{$key}</a>
54
55 {loop="$value.tag_plugin"}
56 {$value}
57 {/loop}
58 </div>
59 {if="isLoggedIn()===true"}
60 <div class="rename-tag-form pure-u-1" style="display:none;">
61 <input type="text" name="{$key}" value="{$key}" class="rename-tag-input" />
62 <a href="#" class="validate-rename-tag"><i class="fa fa-check"></i></a>
63 </div>
64 {/if}
65 </div>
66 {/loop}
67 </div>
68
69 <div id="plugin_zone_end_tagcloud" class="plugin_zone">
70 {loop="$plugin_end_zone"}
71 {$value}
72 {/loop}
73 </div>
74 </div>
75</div>
76
77{include="tag.sort"}
78
79{include="page.footer"}
80</body>
81</html>
82
diff --git a/tpl/default/tag.sort.html b/tpl/default/tag.sort.html
new file mode 100644
index 00000000..89acda0d
--- /dev/null
+++ b/tpl/default/tag.sort.html
@@ -0,0 +1,8 @@
1<div class="pure-g">
2 <div class="pure-u-1 pure-alert pure-alert-success tag-sort">
3 {'Sort by:'|t}
4 <a href="?do=tagcloud" title="cloud">{'Cloud'|t}</a> &middot;
5 <a href="?do=taglist&sort=usage" title="cloud">{'Most used'|t}</a> &middot;
6 <a href="?do=taglist&sort=alpha" title="cloud">{'Alphabetical'|t}</a>
7 </div>
8</div> \ No newline at end of file