def showBacklog(handler, id, search = None, devEdit = False): requirePriv(handler, 'User') id = int(id) sprint = Sprint.load(id) if not sprint or sprint.isHidden(handler.session['user']): ErrorBox.die('Sprints', "No sprint with ID <b>%d</b>" % id) elif not sprint.canView(handler.session['user']): ErrorBox.die('Private', "You must be a sprint member to view this sprint") # Redirect to search help page if searched for empty string if search == '': redirect('/help/search') handler.title(sprint.safe.name) drawNavArrows(sprint, handler.session['user'], '') tasks = sprint.getTasks() editable = sprint.canEdit(handler.session['user']) or (devEdit and isDevMode(handler)) search = Search(sprint, search) print "<script src=\"/settings/sprints.js\" type=\"text/javascript\"></script>" print "<script type=\"text/javascript\">" print "var sprintid = %d;" % id print "var currentUser = %s;" % toJS(handler.session['user'].username if handler.session['user'] else None) print "var totalTasks = %d;" % len(tasks) # True is a placeholder for the dynamic tokens (status, assigned) print "var searchTokens = %s;" % toJS(filter(None, [search.getBaseString() if search.hasBaseString() else None] + [True] + ["%s:%s" % (filt.getKey(), filt.value) for filt in search.getAll() if filt.getKey() not in ('status', 'assigned')])) print "var searchDescriptions = %s;" % toJS(filter(None, ["matching %s" % search.getBaseString() if search.hasBaseString() else None] + [True] + [filt.description() for filt in search.getAll()])) print "TaskTable.init({link_hours_status: %s});" % toJS(not sprint.isPlanning()) print "$('document').ready(function() {" if search.has('assigned'): print " $('%s').addClass('selected');" % ', '.join("#filter-assigned a[assigned=\"%s\"]" % user.username for user in search.get('assigned').users + ([handler.session['user']] if search.get('assigned').currentUser else [])) if search.has('status'): print " $('%s').addClass('selected');" % ', '.join("#filter-status a[status=\"%s\"]" % status.name for status in search.get('status').statuses) print " apply_filters();" print "});" print "</script>" print "<div id=\"selected-task-box\">" print "<span></span>" print Button('history', id = 'selected-history').positive() print Button('highlight', id = 'selected-highlight').positive() print Button('mass edit', id = 'selected-edit').positive() print Button('cancel', id = 'selected-cancel') #.negative() print "</div>" print "<div class=\"backlog-tabs\">" print tabs(sprint, 'backlog') print "<input type=\"text\" id=\"search\" value=\"%s\">" % search.getFullString().replace('"', '"') print "</div>" undelay(handler) print InfoBox('Loading...', id = 'post-status', close = True) avail = Availability(sprint) if sprint.isActive() else None dayStart = Weekday.today().date() print "<div id=\"filter-assigned\">" print "<a class=\"fancy danger\" href=\"#\"><img src=\"/static/images/cross.png\"> None</a>" for member in sorted(sprint.members): cls = ['fancy'] if not sprint.isPlanning() and avail and avail.get(member, dayStart) == 0: cls.append('away') print "<a class=\"%s\" assigned=\"%s\" href=\"/sprints/%d?search=assigned:%s\"><img src=\"%s\"> %s</a>" % (' '.join(cls), member.username, id, member.username, member.getAvatar(16), member.username) print "</div><br>" print "<div id=\"filter-status\">" print "<a class=\"fancy danger\" href=\"#\"><img src=\"/static/images/cross.png\"> None</a>" for status in sorted(statuses.values()): print "<a class=\"fancy\" status=\"%s\" href=\"/sprints/%d?search=status:%s\"><img src=\"%s\">%s</a>" % (status.name, id, status.name.replace(' ', '-'), status.getIcon(), status.text) print "</div><br>" if handler.session['user'].hasPrivilege('Admin') and 'deleted' in sprint.flags: print "<form method=\"post\" action=\"/admin/projects/%d/cancel-deletion/%d\">" % (sprint.project.id, sprint.id) print WarningBox("This sprint is flagged for deletion during nightly cleanup. %s" % Button('Cancel').mini().post()) print "</form>" if sprint.isPlanning(): if sprint.isActive(): print InfoBox("Today is <b>sprint planning</b> — tasks aren't finalized until the end of the day") else: daysTillPlanning = (tsToDate(sprint.start) - getNow()).days + 1 print InfoBox("The sprint has <b>not begun</b> — planning is %s. All changes are considered to have been made midnight of plan day" % ('tomorrow' if daysTillPlanning == 1 else "in %d days" % daysTillPlanning)) elif sprint.isReview(): print InfoBox("Today is <b>sprint review</b> — this is the last day to make changes to the backlog. All open tasks will be deferred at the end of the day") elif not sprint.isOver(): noHours = filter(lambda task: task.stillOpen() and task.hours == 0, tasks) if noHours != []: print WarningBox("There are <a href=\"/sprints/%d?search=status:not-started,in-progress,blocked hours:0\">open tasks with no hour estimate</a>" % sprint.id) tasks = search.filter(tasks) if isDevMode(handler): print Button('#all-tasks borders', "javascript:$('#all-tasks, #all-tasks tr td').css('border', '1px solid #f00').css('border-collapse', 'collapse');").negative() if not editable: print Button('make editable', "/sprints/%d?devEdit" % id).negative() elif devEdit: print Button('make uneditable', "/sprints/%d" % id).negative() print "<div class=\"debugtext\">" print "start: %d (%s)<br>" % (sprint.start, tsToDate(sprint.start)) print "end: %d (%s)<br>" % (sprint.end, tsToDate(sprint.end)) print "</div>" showing = ResponseWriter() print "<span id=\"task-count\"></span>" # save-search href set by update_task_count() print "<a class=\"save-search\"><img src=\"/static/images/save.png\" title=\"Save search\"></a>" print "<a class=\"cancel-search\" href=\"/sprints/%d\"><img src=\"/static/images/cross.png\" title=\"Clear search\"></a>" % id showing = showing.done() print TaskTable(sprint, editable = editable, tasks = tasks, tableID = 'all-tasks', dateline = showing, taskClasses = {task: ['highlight'] for task in (search.get('highlight').tasks if search.has('highlight') else [])}, debug = isDevMode(handler), groupActions = True, taskModActions = True, index = True, goal = True, status = True, name = True, assigned = True, historicalHours = True, hours = True, devEdit = devEdit)