mirror of
https://github.com/flynx/pWiki.git
synced 2025-12-18 17:11:38 +00:00
added basic tag support...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
0df1919259
commit
f58b5fbd56
@ -76,6 +76,8 @@ object.Constructor('BasePage', {
|
||||
type: true,
|
||||
ctime: true,
|
||||
mtime: true,
|
||||
// XXX TAGS HACK -- should this be a list???
|
||||
tags: 'tagstr',
|
||||
},
|
||||
// These actions will be default get :$ARGS appended if no args are
|
||||
// explicitly given...
|
||||
@ -337,6 +339,28 @@ object.Constructor('BasePage', {
|
||||
} else {
|
||||
this.__update__(value) } },
|
||||
|
||||
get tags(){ return async function(){
|
||||
return (await this.data).tags ?? [] }.call(this) },
|
||||
set tags(value){ return async function(){
|
||||
this.data = {
|
||||
...(await this.data),
|
||||
tags: [...value],
|
||||
} }.call(this) },
|
||||
// XXX TAGS HACK -- should this be a list???
|
||||
get tagstr(){ return async function(){
|
||||
return JSON.stringify(await this.tags ?? []) }.call(this) },
|
||||
tag: async function(...tags){
|
||||
this.tags = [...new Set([
|
||||
...(await this.tags),
|
||||
...tags,
|
||||
])]
|
||||
return this },
|
||||
untag: async function(...tags){
|
||||
this.tags = (await this.tags)
|
||||
.filter(function(tag){
|
||||
return !tags.includes(tag) })
|
||||
return this },
|
||||
|
||||
// metadata...
|
||||
//
|
||||
// NOTE: in the general case this is the same as .data but in also allows
|
||||
@ -1263,7 +1287,6 @@ object.Constructor('Page', BasePage, {
|
||||
var that = this
|
||||
var name = args.name //?? args[0]
|
||||
var src = args.src
|
||||
var all = args.all
|
||||
var base = this.get(this.path.split(/\*/).shift())
|
||||
var sort = (args.sort ?? '')
|
||||
.split(/\s+/g)
|
||||
@ -1973,7 +1996,7 @@ module.System = {
|
||||
<a href="#@source(s ../../path)/list">⇑</a>
|
||||
@source(../path)
|
||||
</slot>
|
||||
<macro src="../*:@(all)" join="@source(line-separator)">
|
||||
<macro src="../*:$ARGS" join="@source(line-separator)">
|
||||
<a href="#@source(s ./path)">@source(./name)</a>
|
||||
<sup>
|
||||
<macro src="./isAction">
|
||||
@ -1991,7 +2014,7 @@ module.System = {
|
||||
tree: {
|
||||
text: object.doc`
|
||||
<slot title/>
|
||||
<macro src="../*:@(all)">
|
||||
<macro src="../*:$ARGS">
|
||||
<div>
|
||||
<div class="item">
|
||||
<a class="tree-page-title" href="#@source(s ./path)">@source(./title)</a>
|
||||
@ -1999,12 +2022,12 @@ module.System = {
|
||||
<a class="show-on-hover" href="#@source(s ./path)/delete">×</a>
|
||||
</div>
|
||||
<div style="padding-left: 30px">
|
||||
@include("./tree:@(all)")
|
||||
@include("./tree:$ARGS")
|
||||
</div>
|
||||
</div>
|
||||
</macro>` },
|
||||
all: {
|
||||
text: `@include("../**/path:@(all)" join="@source(line-separator)")`},
|
||||
text: `@include("../**/path:$ARGS" join="@source(line-separator)")`},
|
||||
info: {
|
||||
text: object.doc`
|
||||
<slot pre>
|
||||
@ -2022,6 +2045,8 @@ module.System = {
|
||||
|
||||
type: @source(../type)<br>
|
||||
|
||||
tags: @source(../tags)<br>
|
||||
|
||||
ctime: @source(../ctime)<br>
|
||||
mtime: @source(../mtime)<br>
|
||||
|
||||
@ -2187,7 +2212,7 @@ module.Templates = {
|
||||
text: object.doc`
|
||||
<slot title/>
|
||||
<slot header><content/><a href="#./$NOW/edit">🗎</a></slot>
|
||||
<macro src="*:@(all)" join="<br>">
|
||||
<macro src="*:$ARGS" join="<br>">
|
||||
<div class="item">
|
||||
<a href="#@source(s ./path)/edit">@source(./title)</a>
|
||||
<a class="show-on-hover" href="#@source(s ./path)/info">🛈</a>
|
||||
|
||||
@ -124,16 +124,22 @@ module.BaseStore = {
|
||||
index: async function(action='get', ...args){
|
||||
return index.index(this, ...arguments) },
|
||||
|
||||
// XXX INDEX...
|
||||
//
|
||||
// Format:
|
||||
// [
|
||||
// <path>,
|
||||
// ...
|
||||
// ]
|
||||
//
|
||||
__paths__: async function(){
|
||||
return Object.keys(this.data) },
|
||||
// XXX unique???
|
||||
__paths_merge__: async function(data){
|
||||
return (await data)
|
||||
.concat((this.next
|
||||
&& 'paths' in this.next) ?
|
||||
await this.next.paths
|
||||
: []) },
|
||||
: [])
|
||||
.unique() },
|
||||
__paths_isvalid__: function(t){
|
||||
var changed =
|
||||
!!this.__paths_next_exists != !!this.next
|
||||
@ -159,6 +165,16 @@ module.BaseStore = {
|
||||
get paths(){
|
||||
return this.__paths() },
|
||||
|
||||
//
|
||||
// Format:
|
||||
// {
|
||||
// <name>: [
|
||||
// <path>,
|
||||
// ...
|
||||
// ],
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
__names_isvalid__: function(t){
|
||||
return this.__paths_isvalid__(t) },
|
||||
// NOTE: this is built from .paths so there is no need to define a
|
||||
@ -197,8 +213,76 @@ module.BaseStore = {
|
||||
return this.__names() },
|
||||
|
||||
// XXX tags
|
||||
//
|
||||
// Format:
|
||||
// {
|
||||
// tags: {
|
||||
// <tag>: Set([
|
||||
// <path>,
|
||||
// ...
|
||||
// ]),
|
||||
// ...
|
||||
// },
|
||||
// paths: {
|
||||
// <path>: Set([
|
||||
// <tag>,
|
||||
// ...
|
||||
// ]),
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// XXX should this be here???
|
||||
parseTags: function(str){
|
||||
return str
|
||||
.split(/\s*(?:([a-zA-Z1-9_-]+)|"(.+)"|'(.+)')\s*/g)
|
||||
.filter(function(t){
|
||||
return t
|
||||
&& t != ''
|
||||
&& t != ',' }) },
|
||||
// XXX do we need these???
|
||||
// ...the question is if we have .__tags__(..) how do we
|
||||
// partially .__tags_merge__(..) things???
|
||||
//__tags__: function(){ },
|
||||
//__tags_merge__: function(data){ },
|
||||
__tags_isvalid__: function(t){
|
||||
return this.__paths_isvalid__(t) },
|
||||
__tags: index.makeIndex('tags',
|
||||
async function(){
|
||||
var tags = {}
|
||||
var paths = {}
|
||||
for(var path of (await this.paths)){
|
||||
var t = (await this.get(path)).tags
|
||||
if(!t){
|
||||
continue }
|
||||
paths[path] = new Set(t)
|
||||
for(var tag of t){
|
||||
;(tags[tag] =
|
||||
tags[tag] ?? new Set([]))
|
||||
.add(path) } }
|
||||
return {tags, paths} }, {
|
||||
update: async function(data, path, update){
|
||||
if(!('tags' in update)){
|
||||
return data }
|
||||
var {tags, paths} = await data
|
||||
// remove obsolete tags...
|
||||
this.__tags.options.remove.call(this, data, path)
|
||||
// add...
|
||||
paths[path] = new Set(update.tags)
|
||||
for(var tag of update.tags ?? []){
|
||||
;(tags[tag] =
|
||||
tags[tag] ?? new Set([]))
|
||||
.add(path) }
|
||||
return data },
|
||||
remove: async function(data, path){
|
||||
var {tags, paths} = await data
|
||||
for(var tag of paths[path]){
|
||||
tags[tag].delete(path) }
|
||||
return data }, }),
|
||||
get tags(){
|
||||
return this.__tags() },
|
||||
|
||||
// XXX text search index
|
||||
// XXX text search index (???)
|
||||
|
||||
|
||||
//
|
||||
@ -293,9 +377,16 @@ module.BaseStore = {
|
||||
if(path.includes('*')
|
||||
|| path.includes('**')){
|
||||
var order = (this.metadata(path) ?? {}).order || []
|
||||
|
||||
var {path, args} = pwpath.splitArgs(path)
|
||||
var all = args.all
|
||||
var tags = args.tags
|
||||
tags = typeof(tags) == 'string' ?
|
||||
this.parseTags(tags)
|
||||
: false
|
||||
tags && await this.tags
|
||||
args = pwpath.joinArgs('', args)
|
||||
|
||||
// NOTE: we are matching full paths only here so leading and
|
||||
// trainling '/' are optional...
|
||||
var pattern = new RegExp(`^\\/?`
|
||||
@ -317,6 +408,14 @@ module.BaseStore = {
|
||||
// skip metadata paths...
|
||||
if(p.includes('*')){
|
||||
return res }
|
||||
// skip untagged pages...
|
||||
if(tags){
|
||||
var t = that.tags.paths[p]
|
||||
if(!t){
|
||||
return res }
|
||||
for(var tag of tags){
|
||||
if(!t || !t.has(tag)){
|
||||
return res } } }
|
||||
var m = [...p.matchAll(pattern)]
|
||||
m.length > 0
|
||||
&& (!all ?
|
||||
@ -363,7 +462,6 @@ module.BaseStore = {
|
||||
if(path.includes('*')
|
||||
|| path.includes('**')){
|
||||
var p = pwpath.splitArgs(path)
|
||||
var all = p.args.all
|
||||
var args = pwpath.joinArgs('', p.args)
|
||||
p = pwpath.split(p.path)
|
||||
var tail = []
|
||||
@ -372,12 +470,11 @@ module.BaseStore = {
|
||||
tail = tail.join('/')
|
||||
if(tail.length > 0){
|
||||
return (await this.match(
|
||||
p.join('/') + (all ? ':all' : ''),
|
||||
p.join('/') + args,
|
||||
strict))
|
||||
.map(function(p){
|
||||
all &&
|
||||
(p = p.replace(/:all/, ''))
|
||||
return pwpath.join(p, tail) + args }) } }
|
||||
var {path, args} = pwpath.splitArgs(p)
|
||||
return pwpath.joinArgs(pwpath.join(path, tail), args) }) } }
|
||||
// direct...
|
||||
return this.match(path, strict) },
|
||||
//
|
||||
|
||||
35
pwiki2.js
35
pwiki2.js
@ -17,17 +17,18 @@
|
||||
* - CLI
|
||||
*
|
||||
*
|
||||
* XXX TAGS should ./tags (i.e. .tagstr) return a list of tags???
|
||||
* XXX TAGS
|
||||
* - add tags to page -- macro/filter
|
||||
* - <page>.text -> <page>.tags (cached on .update(..))
|
||||
* - manual
|
||||
* - a way to list tags -- folder like? - ???
|
||||
* - tag cache <store>.tags - DONE
|
||||
* - tag-path filtering... - DONE
|
||||
* XXX TAGS add a more advanced query -- e.g. "/**:tagged=y,z:untagged=x" ???
|
||||
* XXX INDEX DOC can index validation be async???
|
||||
* ...likely no
|
||||
* XXX INDEX add option to set default action (get/lazy/cached)
|
||||
* XXX BUG: when editing the root page of a substore the page's .cache is
|
||||
* not reset for some reason...
|
||||
* ...the problem is in that .names() cache is not reset when a new
|
||||
* page is created...
|
||||
* ...this does not appear to affect normal pages...
|
||||
* ...the root issue is that .__cache_add(..)/.__cache_remove(..)
|
||||
* are called relative to the nested store and not the root...
|
||||
* ...feels like we need to rethink the cache/index strategy globally...
|
||||
* XXX CachedStore seems to be broken (see: pwiki/store/base.js:837)
|
||||
* XXX might be a good idea to create memory store (sandbox) from the
|
||||
* page API -- action??
|
||||
@ -106,24 +107,6 @@
|
||||
* - count + elem-offset
|
||||
* - from + to
|
||||
* XXX revise/update sort...
|
||||
* XXX FEATURE tags: might be a good idea to add a .__match__(..) hook
|
||||
* to enable store-level matching optimization...
|
||||
* ...not trivial to route to alk the stores...
|
||||
* XXX FEATURE tags and accompanying API...
|
||||
* - add tags to page -- macro/filter
|
||||
* - <page>.text -> <page>.tags (cached on .update(..))
|
||||
* - manual
|
||||
* - a way to list tags -- folder like?
|
||||
* - tag cache <store>.tags
|
||||
* format:
|
||||
* {
|
||||
* <tag>: [<path>, ...],
|
||||
* }
|
||||
* - tag-path filtering...
|
||||
* i.e. only show tags within a specific path/pattern...
|
||||
* - path integration...
|
||||
* i.e. a way to pass tags through path...
|
||||
* /some/path:tags=a,b,c
|
||||
* XXX FEATURE images...
|
||||
* XXX async/live render...
|
||||
* might be fun to push the async parts of the render to the dom...
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user