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");