s4
changeset 1040:78e904f9be34
Add Filtering by TEAM
author | HIROSE Yuuji <yuuji@gentei.org> |
---|---|
date | Sun, 21 Apr 2024 11:30:46 +0900 |
parents | 68a01c699acb |
children | 8682149ed229 |
files | examples/common/default/default.css s4-blog.sh s4-funcs.sh s4-main.js |
diffstat | 4 files changed, 108 insertions(+), 13 deletions(-) [+] |
line diff
1.1 --- a/examples/common/default/default.css Sat Apr 06 18:08:10 2024 +0900 1.2 +++ b/examples/common/default/default.css Sun Apr 21 11:30:46 2024 +0900 1.3 @@ -177,9 +177,12 @@ 1.4 display: inline-block; border-radius: 0.8ex; 1.5 } 1.6 td.repl.atall:first-line {font-size: 125%;} 1.7 +p.filter { /* is located after .blog_replies */ 1.8 + margin: 0; padding: 0 1ex; 1.9 +} 1.10 1.11 .blog_replies td.repatt {min-width: 12em;} 1.12 -.blog_replies td.hideauthor {display: none;} 1.13 +.blog_replies td.hideauthor, .hide {display: none;} 1.14 table.blog_replies iframe { 1.15 width: 80%; min-height: 300px; max-width: 50em; max-height: 80vw; 1.16 padding: 0; border: 0;
2.1 --- a/s4-blog.sh Sat Apr 06 18:08:10 2024 +0900 2.2 +++ b/s4-blog.sh Sun Apr 21 11:30:46 2024 +0900 2.3 @@ -164,7 +164,12 @@ 2.4 # blog_m: *article 2.5 2.6 blogowner=`getvalbyid blog owner "$2"` 2.7 - isgroup "$blogowner" && isgroup=true || isgroup=false 2.8 + if isgroup "$blogowner"; then 2.9 + isgroup=true; qgrp=`sqlquote "$blogowner"` 2.10 + else 2.11 + isgroup=false 2.12 + fi 2.13 + 2.14 isgrpadmin=false 2.15 isgrpowner "$user" "$blogowner" && isgrpadmin=true 2.16 [ x"$user" = x"$blogowner" ] && isgrpadmin=true 2.17 @@ -197,7 +202,6 @@ 2.18 *quiz*|*close*|"") # When blog_mode is "", fallback to quiz/close 2.19 f_exclusive=1 2.20 if $isgroup; then 2.21 - qgrp=`sqlquote "$blogowner"` 2.22 if $isgrpadmin; then 2.23 F_UNREADABLE="''" 2.24 else 2.25 @@ -278,7 +282,7 @@ 2.26 hidebtn='<button type="button" id="hideauth" accesskey="a" title="Shortcut: A 2.27 Hide/Show Author - Useful for summary printing 2.28 OFFにするとまとめ印刷に便利。">著者OFF</button>' 2.29 - href4="${blog_math:+<span id=\"mathjax\">Math</span>} $hidebtn <a href=\"#bottom\" accesskey=\"b\" title=\"Shortcut: B${nl}to the Bottom\"> 末尾へ</a>" 2.30 + href4="${blog_math:+<span id=\"mathjax\">Math</span>} $hidebtn <a href=\"#bottom\" accesskey=\"b\" id=\"tailbtn\" title=\"Shortcut: B${nl}to the Bottom\"> 末尾へ</a>" 2.31 $isgrpadmin && 2.32 href5="<a href=\"?blogseen+$rowid\" accesskey=\"s\" title=\"Shortcut: S${nl}State of Accesses\"> 読刻</a>" 2.33 quizmodefile=$tmpd/quiz; rm -f "$quizmodefile" # XXX: Global state 2.34 @@ -445,7 +449,7 @@ 2.35 #CAT="tail -n $nlimit" 2.36 CAT=cat 2.37 limitedmsg="<span class=\"warn\">※最新${nlimit}件のみの表示※</span>" 2.38 - showalllink="<a title=\"Show All\" href=\"?replyblog+$rowid+n:all\">全件表示</a>" 2.39 + showalllink="<a id=\"showall\" title=\"Show All\" href=\"?replyblog+$rowid+n:all\">全件表示</a>" 2.40 cat<<-EOF > $omitline 2.41 <tr class="warn"> 2.42 <th>:<br>$limitedmsg<br>($((narts-$nlimit-1))件省略)<br>:</th> 2.43 @@ -649,6 +653,19 @@ 2.44 title="Shortcut: T${nl}to the Top">先頭へ</a> 2.45 ${showalllink:+/ `echo $showalllink|sed 's/n:all/&\#bottom/'`$limitedmsg}</p> 2.46 EOF 2.47 + # Put team list 2.48 + if $usejson && $isgrp; then 2.49 + echo '<table id="team4filter" style="display: none;">' 2.50 + query<<-EOF 2.51 + .mode html 2.52 + SELECT val, json_group_array(b.rowid) mems 2.53 + FROM grp_mem_m a LEFT JOIN user b ON a.user=b.name 2.54 + WHERE gname = $qgrp AND key='team' 2.55 + GROUP BY val; 2.56 + .mode list 2.57 + EOF 2.58 + echo '</table>' 2.59 + fi 2.60 $iswritable && cat<<-EOF 2.61 <div class="blogcomment"> 2.62 <input type="hidden" name="blogid" value="$id">
3.1 --- a/s4-funcs.sh Sat Apr 06 18:08:10 2024 +0900 3.2 +++ b/s4-funcs.sh Sun Apr 21 11:30:46 2024 +0900 3.3 @@ -93,6 +93,7 @@ 3.4 likeesc=`printf '\037'` # ESCAPE char of LIKE operator 3.5 iconcachekey="profimgcache_S" 3.6 asdelim=":::" # delimiter of dumptable td-class specifier 3.7 +sqlite3 --help 2>&1 | grep -q -- '-json' && usejson=true || usejson=false 3.8 3.9 # Start debug logging 3.10 logtag="($$)${S4WORLD:+{$S4WORLD\}}"
4.1 --- a/s4-main.js Sat Apr 06 18:08:10 2024 +0900 4.2 +++ b/s4-main.js Sun Apr 21 11:30:46 2024 +0900 4.3 @@ -8,6 +8,7 @@ 4.4 mypath = myurl.substring(myurl.lastIndexOf("/")); 4.5 var art_m_list = []; 4.6 var mathjax = false; 4.7 + var filterSelect, filterOption = null; 4.8 let input_pdfsw = 'input[name="comppdf"]'; 4.9 if (mypath.match(/(.*)\/(.*)/)) { 4.10 mypath = RegExp.$2; 4.11 @@ -304,6 +305,9 @@ 4.12 newform = new FormData(form); 4.13 if (data.get("text") > "") { // Called by submit button 4.14 myform.reset(); 4.15 + if (filterOption) { // Restore TEAM filterin if any 4.16 + filterSelect.selectedIndex = filterOption; 4.17 + } 4.18 let pdfsw = myform.querySelector(input_pdfsw); 4.19 if (pdfsw) pdfsw.remove(); 4.20 // myform.text.value = ''; 4.21 @@ -839,16 +843,14 @@ 4.22 let needBOM = e.ctrlKey; 4.23 if (!blogtbl) return; 4.24 let trw = blogtbl.querySelector("tr.warn"), a; 4.25 - if (trw && (a=trw.querySelector("th>a"))) { 4.26 - if (a.title == "Show All") { 4.27 - if (window.confirm(`50件以下に表示制限されています。 4.28 + if (trw && (a=document.getElementById("showall"))) { 4.29 + if (window.confirm(`50件以下に表示制限されています。 4.30 取得し直しますか? 4.31 Cancelを押すとこのまま取得します。 4.32 Seen articles limited to 50 items. 4.33 Push OK to get all articles, Cancel to get only seen articles.`)) { 4.34 - a.click(); 4.35 - return; 4.36 - } 4.37 + a.click(); 4.38 + return; 4.39 } 4.40 } 4.41 if (navigator.userAgent.match(/Windows/)) { 4.42 @@ -964,8 +966,8 @@ 4.43 atMarkView(document); 4.44 /*****************************************************************/ 4.45 //(2) Prepare sort 4.46 - let warn = document.querySelector("tr.warn span.warn"); 4.47 - if (blogheadtd && warn==null) { 4.48 + let limit50 = document.getElementById("showall"); 4.49 + if (blogheadtd && limit50==null) { 4.50 let umode = "byUser", amode = "byArticle"; 4.51 let sbtn = document.createElement("button"); 4.52 sbtn.textContent = umode; 4.53 @@ -1018,6 +1020,78 @@ 4.54 sbtn.textContent = uidsort ? amode : umode; 4.55 }); 4.56 } 4.57 + /*****************************************************************/ 4.58 + //(3) Prepare Filtering by Team 4.59 + let teamfilter = document.getElementById("team4filter"); 4.60 + if (blogheadtd && teamfilter) { 4.61 + let teams = {}; 4.62 + for (let row of teamfilter.querySelectorAll("tr")) { 4.63 + let cols = row.querySelectorAll("td"); 4.64 + if (cols.length == 2) { 4.65 + // <TD>TeamName</TD><TD>["user1@dom1","user2@dom2"]</td> 4.66 + teams[cols[0].textContent] = cols[1].textContent; 4.67 + } 4.68 + } 4.69 + if (Object.keys(teams).length > 0) { 4.70 + function filterArticles(e) { 4.71 + if (limit50) { 4.72 + if (window.confirm(`表示数制限があります。 4.73 +全件表示に切り替えますか。Cencel すると限定表示のまま絞り込みます。 4.74 +Not all articles are seen. 4.75 +Push OK to switch to all-articles mode, Cencel to continue without limit.`)) { 4.76 + limit50.click(); 4.77 + return; 4.78 + } 4.79 + } 4.80 + let userlist; 4.81 + try { 4.82 + userlist = JSON.parse(e.target.value); 4.83 + } catch (e) { 4.84 + return; 4.85 + } 4.86 + let showall = (userlist == ""); 4.87 + let tbl = document.querySelector("table.blog_replies"); 4.88 + for (let row of tbl.querySelectorAll("tr")) { 4.89 + let author = row.querySelector("a[title]"); 4.90 + let visible = true; 4.91 + if (!showall && author) { 4.92 + let home = author.href; 4.93 + if (home && home.match(/home\+([0-9]+)/)) { 4.94 + let uid = RegExp.$1; 4.95 + visible = userlist.find(e=>e==uid) 4.96 + } 4.97 + } 4.98 + if (visible) 4.99 + row.classList.remove("hide") 4.100 + else 4.101 + row.classList.add("hide") 4.102 + filterOption = e.target.selectedIndex; 4.103 + } 4.104 + 4.105 + } 4.106 + // Do Filter Addition 4.107 + let optall = document.createElement("option"); 4.108 + filterSelect = document.createElement("select"); 4.109 + optall.textContent = "-- ALL(全件) --"; 4.110 + optall.value = '""'; // -> showall=true 4.111 + filterSelect.appendChild(optall); 4.112 + filterSelect.addEventListener('change', filterArticles, false) 4.113 + for (let k in teams) { 4.114 + let o = document.createElement("option"); 4.115 + o.textContent = k; 4.116 + o.value = teams[k]; 4.117 + filterSelect.appendChild(o); 4.118 + } 4.119 + let table = document.querySelector("table.blog_replies"); 4.120 + if (table) { 4.121 + let p = document.createElement("p"); 4.122 + p.textContent = "Filter by TEAM: "; 4.123 + p.classList.add("filter"); 4.124 + p.appendChild(filterSelect); 4.125 + table.insertAdjacentElement('beforebegin', p) 4.126 + } 4.127 + } 4.128 + } 4.129 } 4.130 function initGrpAction() { 4.131 var rev = document.getElementById("reverse");