I get the idea from the following page:
But, insted of hard write the pathnames, just use the watch list to populate the grid, then when I was programing I also thought that was interesant to have too pathnames, and search criteria to populate the grid.
The other good thing of the template, it's that it saves the "state" of the pages when you decide, then you can return to the monitoring page and look for what's changed.
You can get more information on this forum thread:
| Version | Date | Author | Description |
| 1.0 | 28-October | carles.coll | First Release |
| 1.1 | 07-November-2009 | carles.coll | - Susbtitute the Last edit resume, for a link to Difference between las review and actual version. - Change the behaviour, all uses AJAX calls, no page reloads. - Show how many changes on (views, comments, attachments) |
| 1.2 | 06-March-2010 | carles.coll | - A change on language behaviour on 9.12 release -> Better language behaviour |
| 1.3 | 10-March-2010 | carles.coll | - Added a loading icon on ajax action. |
| 1.4 | 19-March-2010 | carles.coll | - Mindtouch guys changed an JS api call, Deki.Api -> Mindtouch.Deki , Grrr |
| 1.5 | 24-March-2010 | carles.coll | - Change some CSS copied from neilwPagesMonitor |
For all the examples on this page, the code is shown before the working example. The code is shown with the syntax extension, and looks like this:
PagesMonitor();
This means that the actual code on the page should be enclosed in a DekiScript block. If you want to copy the code from this page, then use the same procedure as described in steps 2-4 above.
| Name | Type | Default | Description |
| page_alerts | bool? | true | For the moment it doesn't work, there's no way to get a list of page alers for a given user. When I can get this list of pages, it will be a second to include here. |
| favorites | bool? | true | It includes all the Whatch List pages |
| paths | List of str? | [] | List of paths to add, they get recursive subpages automatically. |
| search_criteria | str? | "" | Search criteria for pages to add. |
| languages | str? |
| Language PagesMonitor standard texts |
The following example, gives a simple use of this template - just monitor your's Watch Pages (a.k.a. favorites).
PagesMonitor();
/* PagesMonitor
Monitors the changes on pages, it saves the state per user.
get the idea from http://developer.mindtouch.com/index.php?title=User:Neilw/Monitoring by neilw
created by carles.coll, 2009
Version history:
1.00 28-October-2009 First published version
1.01 07-november-2009 - Susbtitute the Last edit resume, for a link to Difference
between las review and actual version.
- Change the behaviour, all uses AJAX calls, no page reloads.
- Show how many changes on (views, comments, attachments)
1.02 06-March-2010 A change on language behaviour on 9.12 release -> Better language behaviour.
1.03 10-March-2010 Added a loading icon on ajax action.
1.04 19-March-2010 Mindtouch guys changed an JS api call, Deki.Api -> Mindtouch.Deki , Grrr
1.05 24-March-2010 Change some CSS copied from neilwPagesMonitor
1.06 30-August-2010 Some minor changes with problems with entity with v10 release
Usage: PagesMonitor(favorites:bool?, paths:list of str?, search_criteria:str?, language:str?)
favorites: (optional) default -> true; It includes all the Whatch List pages.
paths: (optional) default -> []; List of paths to add, they get recursive automatically.
search_criteria: (optional) default -> ''; Search criteria for pages to monitor.
language: (optional) default ->
1rst -> Page language
2nd -> Site language
3rd -> 'en-us'
*/
dekiapi();
// + PARAMETERS
var par = {
favorites: __request.args.favorites?? args.favorites ?? true,
paths: __request.args.paths?? args.paths ?? [],
search_criteria: __request.args.search_criteria ?? args.search_criteria ?? '',
language: __request.args.language ?? args.language ?? ( wiki.language ? wiki.language() : (page.language .. site.languge .. 'en-us') ),
ajax_action: String.tolower( __request.args.ajax_action ?? args.ajax_action ?? 'load')
};
if ((par.ajax_action=='load') || (par.ajax_action=='reload_pagesmonitor')) {
// + CONSTANTS
// -- START LANGUAGE STRINGS
var LANGUAGE_ES = {
title_recursive: web.html("<span class='recursive' title='Incluir subpáginas'> </span>"),
title_page: "Página",
title_cmts: web.html("<span class='comment' title='Número comentarios'> </span>"),
title_lst_cmt: "Ult. Com.",
title_lst_edit: "Ult. Edición",
title_views: web.html("<span class='views' title='Número Visitas'> </span>"),
title_atts: web.html("<span class='attach' title='Número Adjuntos'> </span>"),
title_types: "T",
txt_last_updated: "Fecha última actualización:",
error_no_data_page: "ERROR: no puedo encontrar la pagina con los datos históricos.",
error_updating_history: "ERROR: actualizando el histórico ",
error_creating_history: "ERROR: creando el histórico ",
error_reading_history: "ERROR: leyendo el histórico "
};
var LANGUAGE_EN = {
title_recursive: web.html("<span class='recursive' title='Include subpages'> </span>"),
title_page: "Page",
title_cmts: web.html("<span class='comment' title='Comments number'> </span>"),
title_lst_cmt: "Last comment",
title_lst_edit: "Last edit",
title_views: web.html("<span class='views' title='Viewcount'> </span>"),
title_atts: web.html("<span class='attach' title='Attachments number'> </span>"),
title_types: "T",
txt_last_updated: "Last updated on:",
error_no_data_page: "ERROR: can't find page with data store",
error_updating_history: "ERROR: updating history ",
error_creating_history: "ERROR: creating history ",
error_reading_history: "ERROR: reding history "
};
var TXTS = { 'es-es': LANGUAGE_ES, 'en-us': LANGUAGE_EN, 'en': LANGUAGE_EN };
var lg = par.language;
// -- END LANGUAGE STRINGS
var OBJ_BLANK = {
page: '' ,
views: 0,
cmts: 0,
lastcomment: '',
lastedit: '',
revision: 1
};
var STYLE_DIFF_VALUE = 'background-color: #AAFFAA';
var HISTORY_STORE = 'monitoring_history_'..user.name;
var RECURSIVE_STORE = 'monitoring_recursive_'..user.name;
var ICONOS = '/skins/fiesta/icon_grid_dropdown.png';
var NO_AVATAR = '/@api/deki/files/4913/=NoAvatar.jpg';
var DATE_FORMAT = 'dd/MM/yyyy hh:mm';
// + VARS
/*var path = $3 ?? $path;
var p = (path == nil ? page : wiki.getpage(path));*/
var p = page;
var p_api = null;
if (p == nil) { <p>TXTS[lg].error_no_data_page;</p>;}
else { let p_api = p.api; }
var cache_avatars = {};
var avatar,source,n,a;
var favorites = [];
// + LET'S CODE
// -- Let's search for the notifications
// -- !!! We can't do it for the moment, they are stored on a file, and it's
// -- !!! innaccesible from DekiScript, let's wait for a ne Deki Wiki release...
// -- Let's search for the watchlist
if (par.favorites) {
let favorites ..= [ { path: xml.text(fname), type: "<span class='favoritos' title='favoritos'> </span>", recusive: 0 } foreach var fname in
wiki.api(site.api.."/users/"..user.id.."/favorites")['//path'] ];
}
// -- Let's search for the search criteria
if (par.search_criteria!='') {
var sres = wiki.getsearch(par.search_criteria, 10000, 'title');
foreach(var gs in sres) {
let favorites ..= [ { path: gs.path,
type: "<span class='search' title='busqueda'> </span>",
recursive: 0} ];
}
}
// -- Let's add each path
if (par.paths!=[]) {
foreach(var p in par.paths) {
let favorites ..= [ { path: p,
type: "<span class='recursive' title='ruta'> </span>",
recursive: 3} ];
}
}
// -- Let's eliminate de duplicity
let k = List.GroupBy(favorites,"$.path");
let favorites = [];
foreach(var group in map.keys(k)) {
var types = "";
var rec = 0;
foreach(var val in k[group]) {
let types ..= val.type;
if (val.recursive>rec) { let rec = val.recursive;}
}
let favorites ..= [ { 'path': group, 'types': types, 'recursive': rec} ];
}
var recursive = json.parse(page.properties[RECURSIVE_STORE].text ?? "") ??[];
var pages = [];
var npages = 0;
foreach(var fv in favorites) {
let npages += 1;
var p = wiki.getpage(fv.path);
var r = 0;
if ((list.contains(recursive,""..p.id))||(fv.recursive>0)) {
var tree = wiki.tree(fv.path);
let npages +=#xml.list(tree, ".//li");
let r = 1;
}
let pages ..= [ {'path': fv.path, 'recursive': (fv.recursive>0?fv.recursive:r), 'types': fv.types} ];
}
var data_history = json.parse(page.properties[HISTORY_STORE].text ?? "") ??[];
var data = [];
var data_save = [ { id: 'special', fecha: (date.now) } ];
var dummy = Num.Series(0,npages);
foreach(var dummy_k in dummy) {
if (__index==#pages) { break; }
var act = pages[__index];
var path = act.path;
var p = wiki.getpage(path);
var num_cmts = #p.comments;
var num_atts = #p.files;
var last = num_cmts ? p.comments[num_cmts - 1] : nil;
var hobj = List.Select(data_history,"$.id=='"..p.id.."'");
if (#hobj) { let hobj = hobj[0]; }
else { let hobj = OBJ_BLANK; }
if (act.recursive>0) {
foreach(var k in p.subpages) {
let pages ..= [ { 'path': k.path, 'recursive': 2, 'types': "<span class='recursive' title='Subpágina'> </span>" } ];
}
}
var obj = {
id: p.id ,
page: p.path ,
path: p.path ,
views: p.viewcount,
cmts: num_cmts,
atts: num_atts,
lastcomment: (num_cmts ? ( date.format(last.date,DATE_FORMAT).." by "..last.author.name; ) : ""),
lastedit: ( date.format(p.date, DATE_FORMAT).." by "..p.author.name ),
revision: (#p.revisions)
};
let data_save ..= [ obj ];
// -- Let Search for the avatar and save it to the cache.
var users = [ p.author ];
var un ='';
if (num_cmts) { let users ..= [ last.author ]; }
foreach ( var uns in users ) {
let un = uns.name;
if (!Map.Contains(cache_avatars,un)) {
let source=wiki.getpage('/User:' .. un).files;
let avatar=NO_AVATAR;
foreach (var f in source) {
let n=f.name;
let a=f.api;
if (String.tolower(n) == 'avatar.jpg') { let avatar=f.api; }
}
if (avatar==NO_AVATAR) { let avatar = uns.gravatar; }
let cache_avatars ..= {(un): ("<img src='"..avatar.."' width='30px' valing='top' style='vertical-align: text-top' />") };
}
}
let obj ..= {
views: web.html( (hobj.views!=obj.views ? "<span class='plusCount'>".."+"..(obj.views-hobj.views).."</span>" :"")..(hobj.views?obj.views:"")),
cmts: web.html((hobj.cmts!=obj.cmts? "<span class='plusCount'>".."+"..(obj.cmts-hobj.cmts).."</span>" :"")..(hobj.cmts?obj.cmts:"")),
atts: web.html((hobj.atts!=obj.atts? "<span class='plusCount'>".."+"..(obj.atts-hobj.atts).."</span>" :"")..(hobj.atts?obj.atts:"")),
recursive: (act.recursive<2?
web.html("<input type='checkbox' name='recursive' class='pm_cb_recursive' value='"..obj.id.."' "..(act.recursive>0 ? "checked=1" : nil).." />"):
(act.recursive==3?web.html("<span class='recursive' title='Se incluyen subpaginas'> </span>"):"")),
types: web.html(act.types),
page_style: (hobj.page!=obj.page?STYLE_DIFF_VALUE:''),
views_style: (hobj.views!=obj.views?STYLE_DIFF_VALUE:''),
cmts_style: (hobj.cmts!=obj.cmts?STYLE_DIFF_VALUE:''),
atts_style: (hobj.atts!=obj.atts?STYLE_DIFF_VALUE:''),
lastcomment_style: (hobj.lastcomment!=obj.lastcomment?(last.author.name !=user.name?STYLE_DIFF_VALUE:''):''),
lastedit_style: (hobj.lastedit!=obj.lastedit?(p.author.name!=user.name?STYLE_DIFF_VALUE:''):''),
lastcomment: (num_cmts ? ( <div class='mp_avatar'>web.html(cache_avatars[last.author.name])</div>
<div class='mp_avatar_text'>obj.lastcomment;
collapseItem(<div> last.text </div>)</div> ) : ""),
page: (web.link(p.uri, p.title);
<br /><span style="font-size: 10px; font-color: #eeeeee; font-style: italic">(obj.path)</span>;),
lastedit: ( <div class='mp_avatar'>web.html(cache_avatars[p.author.name])</div>
<div class='mp_avatar_text'>obj.lastedit;' ';
if (hobj.revision!=#p.revisions) { <a href=(uri.build(p.uri,nil,{action:'diff',revision: (hobj.revision??1),diff: (#p.revisions)})) target='_blank'>'Diff'</a> }
</div> )
};
let data ..= [ obj ];
}
let data = list.sort(data,'path');
// + DYNAMIC HTML CONTENT
<div id='monitor_pages'>
<form id='form_page_monitor'>
<input id='button_pm_update_history' type="button" value="Update History" />
if (#data_history>0) {
<span id='pages_monitor_updated' class='mp_last_updated'>" "..TXTS[lg].txt_last_updated.." "..data_history[0].fecha</span>
<span id="pages_monitor_loading" style="display:none;margin:auto;width:40px;height:40px;background-repeat: no-repeat; align: left; background-image:url('/skins/common/icons/anim-wait-circle.gif');"> ; ; ;</span>
}
TSTable {
options: { id: 'ts_pages_monitor', zebra:true },
columns: [
{ title:TXTS[lg].title_recursive,key: "recursive", width:"5%"},
{ title:TXTS[lg].title_page, key: "page", width:"35%" , style:"($.page_style)" },
{ title:TXTS[lg].title_views, key: "views", width:"8%", style:"($.views_style)" },
{ title:TXTS[lg].title_atts, key: "atts", width:"8%", style:"($.atts_style)" },
{ title:TXTS[lg].title_cmts, key: "cmts", width:"8%", style:"($.cmts_style)" },
{ title:TXTS[lg].title_lst_cmt, key:"lastcomment", width:"20%", style:"($.lastcomment_style)" },
{ title:TXTS[lg].title_lst_edit,key:"lastedit", width:"20%", style:"($.lastedit_style)" },
{ title:TXTS[lg].title_types, key:"types" }
],
data: data
};
</form>
<span id='pg_data_save' style='display: none' data=(json.emit(data_save))></span>
</div>
if (par.ajax_action=='load') {
<script type="text/javascript"> "
function pagemonitorWireControls(){
Deki.$('.pm_cb_recursive').bind('click', function() {
var $this = Deki.$(this);
var prop = 'urn:custom.mindtouch.com#' + '"..RECURSIVE_STORE.."';
MindTouch.Deki.ReadPageProperty('"..p_api.."', prop, function(result) {
var data = YAHOO.lang.JSON.parse(result.value || '[]');
if ($this.attr('checked'))
{ data.push($this.attr('value')); }
else {
var i;
var trobat=-1;
for(i=0;i<data.length;i++) {
if (data[i]==$this.attr('value')) { trobat=i; }
}
if (trobat!=-1) { data.splice(trobat,1); }
}
if(result.etag)
MindTouch.Deki.UpdatePageProperty(result.href, YAHOO.lang.JSON.stringify(data), result.etag,
function() {
MindTouch.Deki.Reload($('#monitor_pages'),{ajax_action: 'reload_pagesmonitor'}, function() { pagemonitorWireControlsReload(); });
},
function(result) { alert('"..TXTS[lg].error_updating_history.." (status: ' +
result.status + ' - ' + result.text + ')'); }
);
else
MindTouch.Deki.CreatePageProperty('"..p_api.."', prop, YAHOO.lang.JSON.stringify(data),
function() {
MindTouch.Deki.Reload($('#monitor_pages'),{ajax_action: 'reload_pagesmonitor'}, function() { pagemonitorWireControlsReload(); });
},
function(result) { alert('"..TXTS[lg].error_creating_history.." (status: ' +
result.status + ' - ' + result.text + ')'); }
);
},
function(result) { alert('"..TXTS[lg].error_reading_history.." (status: ' +
result.status + ' - ' + result.text + ')'); }
);
});
Deki.$('#button_pm_update_history').bind('click', function() {
$('#pages_monitor_updated').hide();
$('#pages_monitor_loading').show();
var prop = 'urn:custom.mindtouch.com#' + '"..HISTORY_STORE.."';
MindTouch.Deki.ReadPageProperty('"..p_api.."', prop, function(result) {
var data = Deki.$('#pg_data_save').attr('data');
if(result.etag)
MindTouch.Deki.UpdatePageProperty(result.href, data, result.etag,
function() {
MindTouch.Deki.Reload($('#monitor_pages'),{ajax_action: 'reload_pagesmonitor'}, function() {
pagemonitorWireControlsReload();
});
},
function(result) { alert('"..TXTS[lg].error_updating_history.." (status: ' +
result.status + ' - ' + result.text + ')'); }
);
else
MindTouch.Deki.CreatePageProperty('"..p_api.."', prop, data,
function() {
MindTouch.Deki.Reload($('#monitor_pages'),{ajax_action: 'reload_pagesmonitor'}, function() { pagemonitorWireControlsReload(); });
},
function(result) { alert('"..TXTS[lg].error_creating_history.." (status: ' +
result.status + ' - ' + result.text + ')'); }
);
},
function(result) { alert('"..TXTS[lg].error_reading_history.." (status: ' +
result.status + ' - ' + result.text + ')'); }
);
});
}
function pagemonitorWireControlsReload() {
tstableApply($, false,false,null,'ts_pages_monitor',{},[],['zebra'],[],false);
pagemonitorWireControls();
}
Deki.$(document).ready(function(){
pagemonitorWireControls();
});
" </script>
// + CSS DEFINITION
<style type="text/css">"
#monitor_pages span.mp_last_updated {
font-size:12px;
font-style: italic;
}
#monitor_pages span.favoritos {
width: 28px;
height: 28px;
float: left;
background-image: url("..ICONOS..");
background-position: left -420px;
background-repeat:no-repeat;
}
#monitor_pages span.search {
width: 28px;
height: 28px;
float: left;
background-image: url("..ICONOS..");
background-position: left -60px;
background-repeat:no-repeat;
}
#monitor_pages span.recursive {
width: 28px;
height: 28px;
float: left;
background-image: url("..ICONOS..");
background-position: left -180px;
background-repeat:no-repeat;
}
#monitor_pages span.views {
width: 28px;
height: 28px;
float: left;
background-image: url("..ICONOS..");
background-position: left -120px;
background-repeat:no-repeat;
}
#monitor_pages span.attach {
width: 28px;
height: 28px;
float: left;
background-image: url("..ICONOS..");
background-position: left -300px;
background-repeat:no-repeat;
}
#monitor_pages span.comment {
width: 28px;
height: 28px;
float: left;
background-image: url("..ICONOS..");
background-position: left -510px;
background-repeat:no-repeat;
}
#monitor_pages div.mp_avatar {
margin: 0px;
padding: 0px;
float: left;
width: 25%;
}
#monitor_pages div.mp_avatar_text {
margin: 0px;
padding: 0px;
font-size: 10px;
float: right;
width: 70%;
}
#monitor_pages .plusCount {
padding:2px;
background-color: black;
color:white;
margin-right: 5px;
}
"</style>
} // if (par.ajax_action=='load')...
} // -- if ((par.ajax_action=='load') || (par.ajax_action=='reload_pagesmonitor')) ..
None.
#3 | jonverve at 11/02/2010 - 23:57 I added the following code just below the comment DYNAMIC HTML CONTENT
web.link(uri.build(site.uri .. "Special:Watchedpages"), "Edit your watched pages");
This provides a link to the special page where a user can remove watched pages. Let me know what you think. |
#4 | carles.coll at 12/02/2010 - 06:23 #3: @jonverve - Yes, it's usefull, but only when favorites==true, you can whatch pages by pathnames and then they aren't on the Special:Watchedpages. |
| Images 0 | ||
|---|---|---|
| No images to display in the gallery. |
Copyright © 2011 MindTouch, Inc. Powered by
<div style="float:right;font-size:12px;">web.link(uri.build(site.uri .. "Special:Watchedpages"), "Edit your watched pages");</div>
This provides a link to the special page where a user can remove watched pages. Let me know what you think.