3 of 3 found this page helpful

Interactive Task List

    Introduction

     My company required more than a simple grocery list type Task List, so I laid out some requirements that I though every Task List should have.  Hence the birth of this template(s).  This Task List could be included in Project Pages, Meeting Minutes Pages, Personal Pages, in short where ever you need a Task List.  

    How do I leave feedback or get support?

    To leave feedback or gain support or report a bug please refer to the announcement forum post.

    About this Task List

    Current Features:

    • Parent/Child style task list.  This creates a page for each task under a "Tasks" sub-page.  This allowed for full customization of the task item and also allows for attachments, sub-pages, and Task Notes, etc.
    • AJAX user Assignment Selection.
    • AJAX Task Item Notes Section Viewer from the Task List.
    • Total Progress indicator (Currently Beta).
    • Allows multiple assignee(s) on a single task.
    • Creates a unique ID for each task system wide (Thanks NeilW for this suggestion).
    • The ability to View the Full Task History (Who's, When's, What's).

    Future Features:

    • Some sort of user notification for task assignments or changes
    • Sub-Tasks and/or Milestones
    • Native Task List Sorting/Filtering (Currently no filtering and using TSTables() template for sorting).

    History

    Version Date Author Description
    0.9b.2 10/27/2009 rberinger Changed CSS for progress meter to be a bit less shocking.
    0.9b.1 10/20/2009 rberinger Fixed Centering for notes viewer thanks dekc99 for the solution
    0.9b 10/14/2009   rberinger  Initial Beta Release

    Requirements

    • This template requires MindTouch version: 9.02 or greater
    • jQuery extension needs to be installed
    • Deki AJAX API extension needs to be installed (both these extensions, if installed locally, require additional local .js and other files in res directory under directory where extensions are  located - you can get them here -  http://scripts.mindtouch.com/res/deki/ and http://scripts.mindtouch.com/res/jquery/)
    • LiveQuery jQuery plugin (included on this page).
      • Recommend downloading and saving somewhere on your local wiki for maximum reliablility.
      • Remember to change the reference link location within the template.
    • TSTable() Template.

    How do I install it?

    1. Create a template, call it "Template:ToDoMaster" (or rename as you desire).  You must have UNSAFECONTENT permission for this to work.
    2. Create a "DekiScript" block on the template page (use the "Style" menu in the editor")
    3. Copy the code from the end of this page and paste it into the DekiScript block.  To copy, click "expand source", then mouse over the top right corner of the source code, and click the "view source" button.  This will pop up a window with the source code.  Select all, then copy to clipboard.
    4. Make sure there isn't an extra blank paragraph after the DekiScript block! Do this every time you edit!!!  
    5. Search for http://developer.mindtouch.com and replace with http://your_wiki_site.com
    6. Download the LiveQuery.js file attached to this page and save on your own wiki.  Search for "jquery.livequery.js" and change to the new location on your wiki.
    7. Save.
    8. Follow the steps 1-7 above for Template:ToDoItem.

     

    A quick note about the examples on this page

    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:

    ToDoMaster()

    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. 

    How do I use it?

     There are currently 2 Templates that are required for use of this task list.

    • ToDoMaster (This is the master task list template and should be the only one that should be explicitly refrenced).
    • ToDoItem (This is the template for the actual item,  It will be automatically added by the ToDoMaster template where needed).

    And 1 Optional but recommended Template to obtain full use.

    • ToDoRollup (This is the template that will search the entire wiki site and roll-up the open task items for an individual or manager).  Due to the requirements of this template it will be covered under a separate documentation page.

     

    Suggested Use:

    You can use this list however you see fit or where ever you have need but I will explain how we use it at my company.

    We currently use this task list in 3 different way.  Personal, Project, and Meetings. 

    Personal: Inserted in each users personal area for a personal task list. 

    Project:  Referenced in out Project template for tasks associated to a specific project.

    Meetings:  Referenced in out Meeting Minutes Template for when action items are assigned in a meeting.

     

    Also in each users personal area we inserted the ToDoRollup in the user dashboard area.  This allows for a quick view of every task assigned to a specific user no matter where the task list resides (Project 1, Project 2, Personal, Meetings, etc).  This template can also be used to gather ALL tasks from EVERYONE which is handy for our managers to see what everyone has on their plate.

     

    Arguments

    There are currently no arguments for the ToDoMaster or ToDoItem Templates

    See the ToDoRollup Template documentation page for the details on its arguments.

     

    Examples

    Where ever you need a task list just insert the following In a DekiScript Block or enclosed within the Double Brackets {{}} somewhere on that page.

    ToDoMaster();
    
    Assignee(s):
    Assigner: Start Date:
    Priority: Percent: Due Date:
    Short Description:
    New Task
    Current Task Item(s)
    Total Progress
    Task IDDescriptionAssignee(s)PriorityDue DateProgressOn HoldComplete DateNotes
    34601asdfAnonymous301/06/2010100%false06/23/2011view
    34601asdfpochacco301/06/2010100%false06/23/2011view
    83051just a testAnonymous112/12/201050%trueN/Aview
    83051just a testdsaheu8239372a112/12/201050%trueN/Aview
    98363testing that thing....khaki301/18/201175%trueN/Aview
    73058spank the plankAnonymous211/02/201150%trueN/Aview
    73058spank the plankmickdavidson211/02/201150%trueN/Aview
    40541testingneels103/11/201025%falseN/Aview
    40541testingBDuran103/11/201025%falseN/Aview
    45763stttwinzxc2000107/26/20100%falseN/Aview
    33536play with task list and determine if it will work for usmaphew210/22/2009100%false06/20/2011view
    97401Eldon_Testeneusta1101/14/20110%falseN/Aview
    45784Test Task onejarush.he107/26/20100%falseN/Aview
    44226testerSeGo106/03/201050%falseN/Aview
    41522testtestadminhw304/20/2010100%false08/25/2010view
    41522testtestgouwzee304/20/2010100%false08/25/2010view
    73062create/format notesAnonymous111/04/20100%trueN/Aview
    73062create/format notesmickdavidson111/04/20100%trueN/Aview
    40944Something I need to dobeezoboy104/13/201025%falseN/Aview
    50740Go to bedstuartborn208/20/2010100%false08/31/2010view
    50740Go to bedstu208/20/2010100%false08/31/2010view
    44796where do notes go?tungsten_22106/26/201025%falseN/Aview
    73147test the ToDoMaster in v10jaron111/03/201025%falseN/Aview
    34607This is a test Item to see how priorities workarandall201/06/201050%trueN/Aview
    41586fdfaber104/26/20100%falseN/Aview
    63964Find my loveAnonymous210/07/201025%falseN/Aview
    40908Test Task for Ticket-Systemjhhenkel103/30/20100%falseN/Aview
    40908Test Task for Ticket-Systemfrahe103/30/20100%falseN/Aview
    55741Umlaute not wörkingmjanker109/14/20100%falseN/Aview
    33379I am a test task itemrberinger110/31/20090%falseN/Aview
    39685test to see how to unassignjlpp103/05/20100%falseN/Aview
    39685test to see how to unassignrberinger103/05/20100%falseN/Aview
    35764test!~!winzxc2000202/09/201075%falseN/Aview
    115923oljnkjnojknfthermalpaste108/15/20110%falseN/Aview
    89817blah blahFormidableInc112/21/20100%falseN/Aview
    33982Yet another entry to test functionality...jbnarducci111/20/2009100%false12/14/2009view
    99453qdfsdMseuda101/21/201175%falseN/Aview
    40902test2winzxc2000303/30/201075%trueN/Aview
    40902test2nealw303/30/201075%trueN/Aview
    34268testSatu112/14/200975%false12/14/2009view

    Reference(s)

    LiveQuery jQuery Plugin

    TSTable() Template

    Announcment Forum Post

    Credits/Special Thanks

     Everyone on the forums who helped mold this into what it is today.

    Template/Extension Source Code

    Template:ToDoMaster 

     

    // includes
    dekiapi();
    jquery.ui('smoothness');
    
    <div class="todomaster">
    <form>
    <table bgcolor="#ecf6fc" id="todoedit" cellspacing="1" cellpadding="1" border="1" width="100%" style="table-layout: fixed;">
        <tbody>
            <tr>
                <td valign="middle" rowspan="3"><span style="align:center;">'Assignee(s):'</span>; <br/>;
                    <input type="text" id="user-search" />
                    <div id="user-suggest">
                        <div id="userlist">
                        </div>
                    </div>
                    <select id="assignee" size ="4" multiple="true" style="width:100%">
                        <option value=(user.name) selected='selected'>; user.name; </option>
                    </select>
                </td>
                <td align="right" valign="middle">'Assigner: '; <input id="assigner" type="text" value=(user.name)/>
                </td>
                <td align="right" valign="middle">'Start Date: '; <input id="start" type="text" value=(date.format(date.now, 'MM/dd/yyyy')) ctor="$this.datepicker();"/>
                </td>
            </tr>
            <tr>
                <td align="right" valign="middle">'Priority: '; 
                    <select id="priority">
                        <option value="3" selected=((3 ? 'selected' : nil))>'3'</option>
                        <option value="2" selected=((2 ? 'selected' : nil))>'2'</option>
                        <option value="1" selected=((1 ? 'selected' : nil))>'1'</option>
                    </select>
                    '     Percent: '; 
                    <select id="percent">
                        <option value="5" selected=((5 ? 'selected' : nil))>'100%'</option>
                        <option value="4" selected=((4 ? 'selected' : nil))>'75%'</option>
                        <option value="3" selected=((3 ? 'selected' : nil))>'50%'</option>
                        <option value="2" selected=((2 ? 'selected' : nil))>'25%'</option>
                        <option value="1" selected=((1 ? 'selected' : nil))>'0%'</option>
                    </select>
                </td>
                <td align="right" valign="middle">'Due Date: '; <input id="due" type="text" value=(date.format(date.now, 'MM/dd/yyyy')) ctor="$this.datepicker();"/>
                </td>
            </tr>
            <tr>
                <td align="left" valign="middle" colspan="2">'Short Description: ';<input id="desc" type="text" value="" style="width:30em" maxlength="60" />
                </td>
            </tr>
            <tr>
                <td align="center" valign="middle" colspan="3"> 
                    <input id="save" type="button" value="Save" style="margin-top:15px;margin-bottom:15px" ctor="
                        when($this.click) {
                            $('#assignee option').each(function(i) {
                                $(this).attr('selected','selected');
                            });
    
                            // A bit of Sense Cheking
                            if($('#assignee option').size() == 0){alert('Must have at least 1 Assignee!'); return false;}
                            if($('#desc').val()==''){alert('Must have a Short Description!'); return false;}
                            if($('#due').val()==''){alert('Must have a Due Date!'); return false;}
                            if($('#start').val()==''){alert('Must have a Start Date!'); return false;}
    
    
                            var todoitem  =  ({ assignee:  #assignee.val(), 
                                             assigner:  #assigner.val(), 
                                             startdate: #start.val(),
                                             priority:  #priority.val(),
                                             percent:   #percent.val(),
                                             duedate:   #due.val(),
                                             desc:      #desc.val(),
                                             done:      false,
                                             donedate:  '',
                                             hold:      false,
                                             holdreason:'',
                                             projectpage:({{page.id}}),
                                             whochanged:({{user.name}})
                                           });
                            #save.blur();
                            $('div.todomaster').toggle()
                            
                            Deki.publish('ch_save', todoitem);
                        }
                    " />
                </td>
            </tr>
        </tbody>
    </table>
    </form> 
    </div>
    
    <script type="text/jem">"
    
        var dapi = '/@api/deki/pages/=';
        var dparams = '/contents?abort=never';
        var dpath;
    
        // Create page
        Deki.subscribe('ch_save', null, function(c, m, d) {
    
            // listen for submit events.
            dpath = Deki.url.encode('"..page.path..'/Tasks'.."' + '/' + m['desc'].replace(/ /g,'_').replace(/\\//g,'//'));
            var edpath = Deki.url.encode(Deki.url.encode(dpath));
            var ddata = '<p><a href=" ..page.path ..">Return To: "..page.title.."</a></p><pre class=\\'script\\'> ToDoItem() </pre><p><h2>Notes:</h2></p>';
            Deki.$.ajax({
                type: 'POST',
                url: dapi+edpath+dparams,
                data: ddata,
                complete: function(xhr){
                    if(xhr.status == 200) {    
                        SaveProperty(dpath, m);
                    } else if(xhr.status == 400){
                        var response = '';
                        alert('Bad Requst (400): ' + response);
                    }
                    else if(xhr.status == 403)   alert('Permission denied (403): Are you logged in?');
                    else                         alert('Error ' + xhr.status);
                }
           });
        }, null);
    "</script>
    
    <script type="text/javascript">"
    // Create Property
    var SaveProperty = function(page, properties) {
            var pageapi = '/@api/deki/pages/=' + Deki.url.encode(Deki.url.encode(page));
    
            Deki.Api.CreatePageProperty(pageapi, 'urn:custom.mindtouch.com#todo-item', YAHOO.lang.JSON.stringify(properties), function() {
    //        		alert('Thank you. The information has been submitted.');
                            location.href = '/' + page;
            	}, function(result) {
    if(result.text == '400') {
        alert('Task may already exist.');
    } else {
            		alert('An error occurred trying to create the store (status: ' + result.status + ' - ' + result.text + ')');
    }
            	});
    
    }
    
    "</script>
    
    <html><head>
    
    
    // *** HERE WE START TO DEFINE THE TASK ROLL CALL TABLE ***
    
    // <script type="text/javascript" src="http://jqueryui.com/latest/jquery-1.3.2.js"></script>
    <script type="text/javascript" src="http://developer.mindtouch.com/@api/deki/files/4844/=jquery.livequery.js" />
    <script type="text/javascript">"
        $(document).ready(function() {
    
        $('div.todomaster').hide();
    
            $('div#projectProgress').hide();
            $('div#projectProgress').appendTo('#progressParent');
    
        $('div#TaskDetailViewer').hide();
        $('div#TaskDetailViewer').click(function(){$(this).hide()});
        $(document).click(function(){$('div#TaskDetailViewer').hide()});
        
    
                $('#user-suggest #userlist a').livequery('click', function(event) {
                $('#assignee').append('<option selected=\"selected\" value=\"' + $(this).text() + '\">' + $(this).text() + '</option>');
                $('#user-suggest').hide();
                $('#user-search').val('');
                $('#user-search').focus();
                return false;
            });
            
            $('#assignee').dblclick(function() {
                $('#assignee option:selected').remove();
            });
    
        });
    
    	$('body').ready( function() {
    		$('#user-search').keyup( function() {
                            if ($(this).val()==''){
    				$('#user-suggest').hide();
    				return false;
    			}
    	
    			var q = $(this).val();
    			
    			clearTimeout($.data(this, 'timer'));
    		    var ms = 400; //milliseconds
    		    var wait = setTimeout(function() {
    		      loadusers(q);
    		    }, ms);
    	    
    	
    			$.data(this, 'timer', wait);
    		});
    	});
    
    function loadusers(u) {
        $('#user-suggest').hide();
        var userapi = 'http://developer.mindtouch.com/@api/deki/users?sortby=username&limit=20&activatedfilter=true&usernamefilter=' + u;
    
        jQuery.get(userapi,function(xml){
            // clear the previous results
            $('#user-suggest #userlist div').remove();
    
            // Establish a place holder for our name We need to do this so that we only get 1 unique username per user.
            var theuser;
    
            $(xml).find('users > user').each(function() {
                $(this).find('username').each(function() {
                    theuser = $(this).text();
                    return theuser;
                });
    
                $('#user-suggest #userlist').append('<div class=\"userpick\"><a href=\"#\" id=\"' + theuser + '\">' + theuser + '</a></div>');
            });
        });
        $('#user-suggest').show();
    }
    
    function ShowMyDetails(pid) {
    
        $('div#TaskDetailViewer').hide();
    
        Deki.$.ajax({
                    type: 'GET',
                    url: 'http://developer.mindtouch.com/@api/deki/pages/' + pid + '/contents?section=1&include=true',
                    dataType: 'xml',
                    success: function(data){
                            $('#TaskDetailViewer').html( '<p style=\"text-align:center;\">Click to Close</p>' + $(data).find('content').text() );
                    }    
         });
    
    //      centerIt($('div#TaskDetailViewer'));
    
         $('div#TaskDetailViewer').show();
    }
    
    // function centerIt($el) {
    //	var frm = $('iframe',top.document.body);
    //	var iframeXOffset = 0, iframeYOffset = 0, windowHeight = 0, windowWidth = 0;
    //	var i=frm.length;
    //	while (i--) {
    //		if (frm[i].contentDocument) {
    //			doc = frm[i].contentDocument;
    //		} else {
    //			doc = frm[i].contentWindow.document;
    //		}
    //		if (doc === document) {
    //			//located our iframe!
    //			iframeXOffset = $(frm[i]).offset().left;
    //			iframeYOffset = $(frm[i]).offset().top;
    //			break;
    //		}
    //	};
    //	if (jQuery.browser.msie) {
    //		windowWidth = top.window.document.documentElement.clientWidth;
    //		windowHeight = top.window.document.documentElement.clientHeight;
    //	} else {
    //		windowWidth = top.window.innerWidth;
    //		windowHeight = top.window.innerHeight;
    //	}
    //	var elHeight = $el.height();
    //	var newTop = ((windowHeight/2) - (elHeight/2)) - iframeYOffset + $(parent.document.documentElement).scrollTop();
    //	if ((newTop + elHeight) > $(document).height()) {
    //		newTop = $(document).height() - elHeight;
    //	}
    //	$el.css ({
    //		left: ((windowWidth/2) - ($el.width()/2)) - iframeXOffset + $(parent.document.documentElement).scrollLeft(),
    //		top: newTop
    //	});
    // }
    
    "</script>
    <style type="text/css">"
        #user-suggest {
                        width=650px;
                        position:absolute;
                        border:1px solid #ccc;
                        background:#fff;
                        z-index:50;
                      }
    
        #user-suggest div#userlist div.userpick:hover {
                        text-decoration: underline; 
                        background-color:#0099CC;
                     }
        #user-suggest div#userlist div.userpick a {
                        text-decoration: none; 
                     }
        #user-suggest div#userlist div.userpick {
                        text-decoration: none; 
                        background-color:#FFFFcc;
                        padding-left:10px;
                        padding-right: 10px;
                     }
    
    div#TaskDetailViewer{
    		position:fixed;
    		z-index:100;
    		border:1px solid #000000;
    		margin: auto;
    		overflow: auto;
    		background-color:#FFFFCC;
    		height: 400px;
    		/*width: 600px;*/
    		padding: 5px;
    		top: 25%;
    		left: 25%;
    		width: 50%;
    		display: none; /* so the element does not get rendered when the page loads. */
    	}
        div#projectProgress {
                width:155px;
                float: right;
                position:absolute;
                margin-top:25px;
                overflow:hidden;
            }
        div#progressParent {
                width:155px;
                margin-bottom: 5px;
                float: right;
                background-color:#FFFFFF;
                position:relative;
            }
    
    "</style>
    </head></html>
    <div style="width:100%;">
    </div>
    <table border="0" width="100%">
        <tbody>
            <tr>
                <td style="vertical-align:bottom; width:33%;">
                    <div><strong><a style="float:left"  href="#" ctor="$(this).click(function(){$('div.todomaster').toggle(); return false;});" >'New Task'</a></strong></div>
                </td>
                <td style="vertical-align: bottom; text-align:center; width:33%">
                    <div><strong><a href="#" ctor="$(this).click(function(){$('div.todolist').toggle(); return false;});" >'Current Task Item(s)'</a></strong></div>
                </td>
                <td style="vertical-align:bottom; text-align:center;">
                    <div id="progressParent"><strong><a href="#" style="float:right" ctor="when($this.click) {return false;}; when($this.mouseover){ $('div#projectProgress').show()}; when($this.mouseout) {$('div#projectProgress').hide()}">'Total Progress'</a></strong></div>
                </td>
            </tr>
        </tbody>
    </table>
    
    var percentmap = {1:'0%',2:'25%',3:'50%',4:'75%',5:'100%',default:'0%'};
    var prioritymap = {1:'1',2:'2',3:'3',default:'Unk'};
    var CurProgress = 0;
    var ProgressPossible=0;
    var OverAllProgress=0;
    
    <div class="todolist">
    if(wiki.pageexists(page.path .."/Tasks") || #wiki.pageexists(page.path .."/Tasks")>0) {
    tstable();
    <table id="TaskItems" border="1" width="100%">
        <tr>
            <th>
                'Task ID'
            </th>
            <th>
                'Description'
            </th>
            <th>
                'Assignee(s)'
            </th>
            <th>
                'Priority'
            </th>
            <th>
                'Due Date'
            </th>
            <th>
                'Progress'
            </th>
            <th>
                'On Hold'
            </th>
            <th>
                'Complete Date'
            </th>
            <th>
                'Notes'
            </th>
        </tr>
    
        foreach(var taskitem in wiki.getpage(page.path ..'/Tasks').subpages) {
    
            var taskdetail = json.parse(taskitem.properties['todo-item'].text ?? "") ?? [];
    
    foreach(var a in taskdetail.assignee) {
        let ProgressPossible = ProgressPossible + 4;
        let curprogress = (curprogress + taskdetail.percent -1);
    
            <tr>
                <td>
                    web.link(taskitem.uri, taskitem.id);
                </td>
                <td>
                    taskdetail.desc;
                </td>
                <td>
                    a;
                </td>
                <td>
                    prioritymap[((taskdetail.priority==nil || taskdetail.priority == '') ? 'default' : taskdetail.priority)];
                </td>
                <td>
                    taskdetail.duedate;
                </td>
                <td>
                    percentmap[((taskdetail.percent==nil || taskdetail.percent=='') ? 'default' : taskdetail.percent)];
                </td>
                <td>
                    (taskdetail.hold==true);
                </td>
                <td>
                    (date.isvalid(taskdetail.donedate) ? date.format(taskdetail.donedate,'MM/dd/yyyy') : 'N/A');
                </td>
                <td>
                    <a href="#" id=(taskitem.id ..'-'..__index) taskid=(taskitem.id) ctor="when($this.click){ ShowMyDetails($this.attr('taskid')); return false; };" >'view'</a>
                </td>
            </tr>
    }
        }
    
    </table> 
    
    } else {
    <div>'No Tasks Yet Created...'</div>
    }
    </div>
    
    let OverAllProgress = ( num.round( ((curprogress / progresspossible) * 100), 0) );
    <div id="projectProgress">
    //    GoogleStatusIndicator{value: OverAllProgress, text: '' ..OverAllProgress ..'%', height: '200', width: '120'};
    var text = '' ..OverAllProgress ..'%';
    // web.image('http://chart.apis.google.com/chart?chs=200x120&cht=gom&chd=t:' ..OverAllProgress ..'&chl=' ..web.uriencode(text));
    var theUrl = 'http://chart.apis.google.com/chart?chs=200x120&cht=gom&chd=t:' ..OverAllProgress ..'&chl=' ..web.uriencode(text);
    
    <img style="margin-top:-15px;" src=(theUrl) />
    
    </div>
    
    <div id="TaskDetailViewer" height="30%"></div>
    

    Template:ToDoItem

     

     dekiapi();
    jQuery.ui("smoothness");
    <script type="text/javascript">"
    "</script>
    var todo = json.parse(page.properties['todo-item'].text ?? "") ?? [];
    'This task has been assigned and ID of: ' ..page.id;
    <div>
    <form>
    <input id="projpage" type="hidden" value=(todo.projectpage) />
    <table bgcolor="#f0ffff" id="subtodoedit" cellspacing="1" cellpadding="1" border="1" width="100%" style="table-layout: fixed;">
        <tbody>
            <tr>
                <td valign="middle" rowspan="4"><span style="align:center;">'Assignee(s):'</span>; <br/>;
                    <input type="text" id="user-search" />
                    <div id="user-suggest">
                        <div id="userlist">
                        </div>
                    </div>
                    <select id="subassignee" size ="4" multiple="true" style="width:100%">
                        foreach(var u in todo.assignee) {
                            <option value=(u) selected=('selected')>u</option>
                        }
                    </select>
                </td>
                <td align="right" valign="middle">'Assigner: '; <input id="subassigner" type="text" value=((todo.assigner ?? user.name)) disabled=((todo.assigner==nil) ? nil : 'disabled') />
                </td>
                <td align="right" valign="middle">'Start Date: '; <input id="substart" type="text" value=(((todo.startdate==nil) ?  date.format(date.now, 'MM/dd/yyyy') : todo.startdate)) disabled=((todo.startdate==nil) ? nil : 'disabled') ctor="$this.datepicker();"/>
                </td>
            </tr>
            <tr>
                <td align="right" valign="middle">'Priority: '; 
                    <select id="subpriority">
                        <option value="3" selected=(((todo.priority == 3) ? 'selected' : nil))>'3'</option>
                        <option value="2" selected=(((todo.priority == 2) ? 'selected' : nil))>'2'</option>
                        <option value="1" selected=(((todo.priority == 1) ? 'selected' : nil))>'1'</option>
                    </select>
                    '     Percent: '; 
                    <select id="subpercent">
                        <option value="5" selected=(((todo.percent == 5) ? 'selected' : nil))>'100%'</option>
                        <option value="4" selected=(((todo.percent == 4) ? 'selected' : nil))>'75%'</option>
                        <option value="3" selected=(((todo.percent == 3) ? 'selected' : nil))>'50%'</option>
                        <option value="2" selected=(((todo.percent == 2) ? 'selected' : nil))>'25%'</option>
                        <option value="1" selected=(((todo.percent == 1) ? 'selected' : nil))>'0%'</option>
                    </select>
                </td>
                <td align="right" valign="middle">'Due Date: '; <input id="subdue" type="text" value=(((todo.duedate==nil) ? date.format(date.now, 'MM/dd/yyyy') : todo.duedate)) ctor="$this.datepicker();"/>
                </td>
            </tr>
            <tr>
                <td align="left" valign="middle" colspan="2">'Short Description: ';<input id="subdesc" type="text" value=(((todo.desc==nil) ? "" : todo.desc)) disabled=((todo.desc==nil) ? nil : 'disabled') style="width:30em" maxlength="40" />
                </td>
            </tr>
    <tr>
    <td>
        'Task On Hold: '; <input type="checkbox" id="onhold" value="" checked=(((todo.hold == true) ? 'checked' : nil)) />;
        <br/>;
        'Reason: '; <input type="text" id="holdreason" value=(((todo.holdreason==nil) ? "" : todo.holdreason)) />;
    </td>
    <td>
        'Task Complete: '; <input type="checkbox" id="tododone" value="" checked=(((todo.done == true) ? 'checked' : nil)) ctor="
                                when($this.click) {
                                    if($this.attr('checked')){
                                        if(#subpercent.val() != '5') {
                                            alert('Task not marked 100% done');
                                            $this.attr('checked', false)
                                            return;
                                        } else {
                                            #onhold.attr('checked',false);
                                            #holdreason.attr('value', '');
                                            #donedate.attr('value', ({{date.format(date.now, 'MM/dd/yyyy')}}));
                                        }
                                    } else {
                                        #donedate.attr('value', (''));
                                    }
                                }
                                "/>;
        <br/>;
        'Done Date: '; <input type="text" id="donedate" value=(((todo.donedate==nil) ? "" : todo.donedate )) />;
    </td>
    </tr>
            <tr>
                <td align="center" valign="middle" colspan="3"> 
                    <input id="subupdatetodo" type="button" value="Update" style="margin-top:15px;margin-bottom:15px" ctor="
                        when($this.click) {
                            $('#subassignee option').each(function(i) {
                                $(this).attr('selected','selected');
                            });
    
                            var todoitem  =  ({ assignee: #subassignee.val(), 
                                             assigner:    #subassigner.val(), 
                                             startdate:   #substart.val(),
                                             priority:    #subpriority.val(),
                                             percent:     #subpercent.val(),
                                             duedate:     #subdue.val(),
                                             desc:        #subdesc.val(),
                                             done:        #tododone.attr('checked'),
                                             donedate:    #donedate.val(),
                                             hold:        #onhold.attr('checked'),
                                             holdreason:  #holdreason.val(),
                                             projectpage: #projpage.val(),
                                             whochanged:  {{user.name}}
                                           });
                            #save.blur();
    
                            Deki.publish('ch_updatetodo', todoitem);
                        }
                    " />
                </td>
            </tr>
        </tbody>
    </table>
    </form> 
    </div>
    
    
    
    <script type="text/jem">"
        var dapi = '/@api/deki/pages/=';
        var dpath = Deki.url.encode('"..page.path.."');
    
        Deki.subscribe('ch_updatetodo',null, function(c, m, d) {
    
            UpdateProperty(dpath,m);
    
        }, null);
    "</script>
    
    <script type="text/javascript">"
    // Create Property
    var SaveProperty = function(page, properties) {
            var pageapi = '/@api/deki/pages/=' + Deki.url.encode(Deki.url.encode(page));
    
            Deki.Api.CreatePageProperty(pageapi, 'urn:custom.mindtouch.com#todo-item', YAHOO.lang.JSON.stringify(properties), function() {
            		alert('Thank you. The information has been submitted.');
            	}, function(result) {
            		alert('An error occurred trying to create the store (status: ' + result.status + ' - ' + result.text + ')');
            	});
    
    }
    
    
    var UpdateProperty = function(page, properties) {
    
            var pageapi = '/@api/deki/pages/=' + Deki.url.encode(Deki.url.encode(page));
    
            Deki.Api.ReadPageProperty(null, 'urn:custom.mindtouch.com#todo-item', function(result) {
                if(result.etag) {    // page property exists, write over it.
                   Deki.Api.UpdatePageProperty(result.href, YAHOO.lang.JSON.stringify(properties), result.etag, function() {
                        alert('Your task has been updated!');
                    }, function(result) {
    	            alert('An error occurred trying to update the store (status: ' + result.status + ' - ' + result.text + ')');
    	        });
    	    } 
            }, function(result) {
    	    alert('An error occurred trying to read the store (status: ' + result.status + ' - ' + result.text + ')');
            });
    }
    
    "</script> 
    
    <html><head>
    // <script type="text/javascript" src="http://jqueryui.com/latest/jquery-1.3.2.js"></script>
    <script type="text/javascript" src="http://developer.mindtouch.com/@api/deki/files/4844/=jquery.livequery.js" />
    <script type="text/javascript">"
        $(document).ready(function() {
            $('#user-suggest #userlist a').livequery('click', function(event) {
                $('#subassignee').append('<option selected=\"selected\" value=\"' + $(this).text() + '\">' + $(this).text() + '</option>');
                $('#user-suggest').hide();
                $('#user-search').val('');
                $('#user-search').focus();
                return false;
            });
            
            $('#subassignee').dblclick(function() {
                $('#subassignee option:selected').remove();
            });
    
        });
    
    	$('body').ready( function() {
    		$('#user-search').keyup( function() {
                            if ($(this).val()==''){
    				$('#user-suggest').hide();
    				return false;
    			}
    	
    			var q = $(this).val();
    			
    			clearTimeout($.data(this, 'timer'));
    		    var ms = 400; //milliseconds
    		    var wait = setTimeout(function() {
    		      loadusers(q);
    		    }, ms);
    	    
    	
    			$.data(this, 'timer', wait);
    		});
    	});
    
    function loadusers(u) {
        $('#user-suggest').hide();
        var userapi = 'http://developer.mindtouch.com/@api/deki/users?sortby=username&limit=20&activatedfilter=true&usernamefilter=' + u;
    
        jQuery.get(userapi,function(xml){
            // clear the previous results
            $('#user-suggest #userlist div').remove();
    
            // Establish a place holder for our name We need to do this so that we only get 1 unique username per user.
            var theuser;
    
            $(xml).find('users > user').each(function() {
                $(this).find('username').each(function() {
                    theuser = $(this).text();
                    return theuser;
                });
    
                $('#user-suggest #userlist').append('<div class=\"userpick\"><a href=\"#\" id=\"' + theuser + '\">' + theuser + '</a></div>');
            });
        });
        $('#user-suggest').show();
    }
    
    "</script>
    <style type="text/css">"
        #user-suggest {
                        width=650px;
                        position:absolute;
                        border:1px solid #ccc;
                        background:#fff;
                        z-index:50;
                      }
    
        #user-suggest div#userlist div.userpick:hover {
                        text-decoration: underline; 
                        background-color:#0099CC;
                     }
        #user-suggest div#userlist div.userpick a {
                        text-decoration: none; 
                     }
        #user-suggest div#userlist div.userpick {
                        text-decoration: none; 
                        background-color:#FFFFcc;
                        padding-left:10px;
                        padding-right: 10px;
                     }
    "</style>
    </head></html>
    
    
    <html><head>
    <script type="text/javascript">"
        $(document).ready(function() {
            $('#taskhistory').hide();
    
            $('a#showhistory').click(function() {
               $('#taskhistory').toggle('slow'); 
            });
    
        });
    "</script>
    <style type="text/css">"
        #taskhistory{
            border:1px solid #CCCC33;
            padding:10px;
            margin-top:10px;
            width:500px;
            font-size:10px; 
            overflow:scroll;
        }
    "</style>
    </head></html>
    
    
    // *** HERE WE DEFINE THE BODY OF THE TASK ITEM ***
    
    var prevprop = {};
    var summary = '';
    var curprop = {};
    var validkeys =['assignee','assigner','startdate','priority','percent','duedate','desc','done','donedate','hold','holdreason'];
    
    <p>
    <a href="#" id="showhistory">'Task History'</a>
    </p>
    <div id="taskhistory" class="hideable">
        foreach(var prop in page.properties) {
            foreach(var rev in prop.revisions) {
                if(rev.name == 'todo-item') {
    //                date.format(rev.date,'u'); ' - '; rev.revision; <br/>; json.parse(rev.text);
    let curprop = json.parse(rev.text);
    let summary = '<b>' ..curprop.whochanged ..'</b> updated this task on:  ' ..date.format(rev.date,'u') ..'<br/>';
    
    if(rev.revision == 1) {
        // This is the initial created task
        let prevprop = curprop;
        let summary ..= '<b>Task Created!</b><br/>'
    } else {
        // Compare against previous property
        foreach(var k in validkeys) {
            if(typeof curprop[k] == 'list') {
                var prevlistprop = list.sort(prevprop[k]);
                foreach(var li in list.sort(curprop[k])) {
                    if(li not in prevlistprop) {
                        let summary ..= ((curprop[k] == prevprop[k]) ? '' : 'Changed: <b>\"' ..k ..'\"</b> New Value(s): <b>\"' ..curprop[k] ..'\"</b><br/>');
                        break;
                    }
                }
            } else {
                let summary ..= ((curprop[k] == prevprop[k]) ? '' : 'Changed: <b>\"' ..k ..'\"</b> New Value(s): <b>\"' ..curprop[k] ..'\"</b><br/>');
            }
        }
        let prevprop = curprop;
    }
    web.html(summary); <br/>;
    
                }
            }
        }
    </div>

     

     

     

    Disclaimers

    None.

    Was this page helpful?
    Tag page

    Files 1

    FileVersionSizeModified 
    Viewing 13 of 13 comments: view all
    Thanks for sharing this with the community rberinger.

    I'm having trouble getting the ToDoMaster template working on MindTouch Core v9.08.3. I've installed the jQuery and Deki AJAX API extensions per the instructions on their pages. I've also created the TsTable template and that works without error. When I copy the ToDoMaster template source, create a new template, switch to Source mode, change the H1 to contain Template:ToDoMaster, add the <pre class="script"></pre>, paste the template source inbetween the pre tags and save, I get the error "pre, line 5, column 83: EOF expected". This is on saving the template. I've tried the same but also replacing the developer.mindtouch.com URLs to be my local server's and get the same error so I don't think that's the issue. I've tried ignoring the error and using the template on another page and get a similar EOF error on that page.

    Does anyone know what I'm doing wrong?

    Another thing I tried was manually manipulating the source to see on what line it was failing. The first line that generates an error when saving the template is line 29 (of the template source provided on this site). That is <option value="2" selected=((2 ? 'selected' : nil))>'2'</option>. I tried adding a semi-colon after the '2' and that got it past that line. I don't know if that's a useful hint.

    Any help would be really appreciated.

    Thanks!
    Posted 16:23, 4 Mar 2010
    I have the same problem with this extension that Dimitri describes to "Integrated Bug and Issue Tracker" extension here:

    http://developer.mindtouch.com/App_Catalog/Integrated_Bug_and_Issue_Tracker#comment21

    Both extensions use dekiapi. ¿Maybe the source of the bug?
    Posted 05:19, 8 Mar 2010
    @tsao @jlpp have you solved the problems?

    what language are you running Mindtouch under? I've have had problems with templates when it's set to danish, but they work if the language is English (US)
    Posted 22:08, 12 Apr 2010
    I had problems setting to Spanish. I had to solve the problem touching the database directly.
    Posted 06:15, 24 Apr 2010
    @tsao what did u do in order to get it to work?
    Posted 04:40, 29 May 2010
    Interestingly enough, I can't get the "view" link to work under the Notes column. The yellow box just comes up as blank.
    Posted 12:46, 17 Jun 2010
    Looks like the example on this page is broken - due to v10 update?
    Posted 10:10, 25 Aug 2010
    @sieben thx for pointing it out. the syntax error is fixed now
    Posted 12:40, 25 Aug 2010
    SteveB, what did you actually fix? I'm having same problem on my wiki after upgrading
    Posted 03:54, 27 Aug 2010
    @schnaaf there were some missing ';' chars. check out the page history to see the change.
    Posted 06:24, 27 Aug 2010
    The task list is not working if the new task contains umlaut or special characters.
    Posted 02:25, 14 Sep 2010
    The task list is not working if the new task contains umlaut or special characters.
    Posted 02:25, 14 Sep 2010
    very nice application. it works but isn't able to find the users with JQuery. can you give me some help?
    thanks
    danilo edited 09:07, 31 Jan 2011
    Posted 09:07, 31 Jan 2011
    Viewing 13 of 13 comments: view all
    You must login to post a comment.

    Copyright © 2011 MindTouch, Inc. Powered by