Exemple #1
0
def searchSavedOthers(handler):
	handler.title('Saved Searches')
	requirePriv(handler, 'User')
	print tabs.where('others')
	undelay(handler)

	print "<style type=\"text/css\">"
	print ".other-search {padding-bottom: 4px; border-bottom: 1px dashed #000;}"
	print ".other-search h2 {margin-bottom: 4px;}"
	print ".other-search small {float: right; font-weight: normal; font-size: 12pt;}"
	print ".other-search code {font-size: 14pt;}"
	print "</style>"

	searches = filter(lambda search: search.user != handler.session['user'] and search.public, SavedSearch.loadAll(orderby = 'name'))
	if searches == []:
		print "No shared searches available"
	else:
		for search in searches:
			print "<div class=\"other-search\">"
			print "<h2>%s<small><img class=\"bumpdown\" src=\"%s\">&nbsp;%s</small></h2>" % (search.safe.name, search.user.getAvatar(16), search.user.username)
			print "<code>%s</code><br><br>" % search.safe.query

			following = handler.session['user'] in search.followers
			print "<form method=\"post\" action=\"/search/saved/%d/%s\">" % (search.id, 'unfollow' if following else 'follow')
			print Button('Run', url = "/search/saved/%d/run" % search.id)
			btn = Button('Unfollow' if following else 'Follow', type = 'submit')
			if following:
				btn.negative()
			else:
				btn.positive()
			print btn
			print "</form>"
			print "</div>"
Exemple #2
0
def projectsCalendar(handler):
    undelay(handler)

    print '<link href="/static/fullcalendar.css" rel="stylesheet" type="text/css" />'
    print '<script src="/static/fullcalendar.js" type="text/javascript"></script>'
    print '<script src="/static/projects-calendar.js" type="text/javascript"></script>'
    print '<div id="calendar"></div>'
Exemple #3
0
def adminPrivileges(handler, username = None):
	handler.title("Privileges")
	requirePriv(handler, 'Admin')
	undelay(handler)

	users = User.loadAll(orderby = 'username')
	counts = {name: len(filter(lambda user: name in user.privileges, users)) for name in privList}

	if username:
		print "<style type=\"text/css\">"
		print "table.granttable tr[username=%s] {" % username
		print "    background-color: #faa;"
		print "}"
		print "</style>"

	print "<h3>List</h3>"
	print "<table border=\"0\" cellspacing=\"4\">"
	print "<tr><th>Name</th><th>Grants</th><th>Description</th></tr>"
	for name, desc in privList.iteritems():
		print "<tr><td>%s</td><td>%d</td><td>%s</td></tr>" % (name, counts[name] if name in counts else 0, desc)
	print "</table>"

	print "<h3>Grants</h3>"
	print "<form method=\"post\" action=\"/admin/privileges\">"
	print "<table border=\"0\" cellspacing=\"0\" cellpadding=\"2\" class=\"granttable\">"
	print "<tr><td>&nbsp;</td>%s</tr>" % ''.join("<td>%s</td>" % name for name in privList)
	for user in users:
		print "<tr username=\"%s\">" % user.username
		print "<td>%s</td>" % user.username
		for name in privList:
			print "<td><input type=\"checkbox\" name=\"grant[%s][%s]\"%s></td>" % (user.username, name, ' checked' if user.hasPrivilege(name) else '')
		print "</tr>"
	print "<tr><td>&nbsp;</td><td colspan=\"3\">%s</td></tr>" % Button('Save', type = 'submit').positive()
	print "</table>"
	print "</form>"
Exemple #4
0
def searchSaved(handler):
	handler.title('Saved Searches')
	requirePriv(handler, 'User')
	print tabs.where('yours')
	undelay(handler)

	searches = SavedSearch.loadAll(userid = handler.session['user'].id)
	if searches != []:
		print "<table border=0 cellspacing=4>"
		print "<tr><th>Name</th><th>Query</th><th>Shared</th><th>&nbsp;</th></tr>"
		for search in searches:
			print "<form method=\"post\" action=\"/search/saved/%d/update\">" % search.id
			print "<tr>"
			print "<td><input type=\"text\" name=\"name\" value=\"%s\"></td>" % search.name.replace('"', '&quot;')
			print "<td style=\"width: 100%%\"><input type=\"text\" name=\"query\" value=\"%s\" style=\"width: 100%%\"></td>" % search.query.replace('"', '&quot;')
			print "<td style=\"text-align: center\"><input type=\"checkbox\" name=\"share\"%s></td>" % (' checked' if search.public else '')
			print "<td nowrap>"
			print "<button onClick=\"document.location = '/search/saved/%d/run'; return false;\" class=\"fancy\">run</button>" % search.id
			print "<button type=\"submit\" class=\"fancy\" name=\"action\" value=\"update\">update</button>"
			print "<button type=\"submit\" class=\"fancy danger\" name=\"action\" value=\"delete\">delete</button>"
			print "</td>"
			print "</tr>"
			print "</form>"
		print "</table>"

	print "<h3>New Search</h3>"
	newSearchForm()
Exemple #5
0
def adminProjects(handler):
	handler.title('Project Management')
	requirePriv(handler, 'Admin')

	print "<style type=\"text/css\">"
	print "table.list td.right > * {width: 400px;}"
	print "table.list td.right button {width: 200px;}" # Half of the above value
	print "</style>"

	undelay(handler)

	print "<h3>New Project</h3>"
	print "<form method=\"post\" action=\"/admin/projects\">"
	print "<table class=\"list\">"
	print "<tr><td class=\"left\">Name:</td><td class=\"right\"><input type=\"text\" name=\"name\"></td></tr>"
	print "<tr><td class=\"left\">&nbsp;</td><td class=\"right\">"
	print Button('Save', id = 'save-button', type = 'submit').positive()
	print Button('Cancel', type = 'button', url = '/admin').negative()
	print "</td></tr>"
	print "</table>"
	print "</form>"

	print "<h3>Current Projects</h3>"
	for project in Project.getAllSorted(handler.session['user']):
		print "<a href=\"/admin/projects/%d\">%s</a><br>" % (project.id, project.safe.name)
Exemple #6
0
def adminUsers(handler):
	handler.title('User Management')
	requirePriv(handler, 'Admin')

	print "<style type=\"text/css\">"
	print "table.list td.right > * {width: 400px;}"
	print "table.list td.right button {width: 200px;}" # Half of the above value
	print "</style>"
	undelay(handler)

	print "<h3>New User</h3>"
	print "<form method=\"post\" action=\"/admin/users\">"
	print "<input type=\"hidden\" name=\"action\" value=\"new\">"
	print "<table class=\"list\">"
	print "<tr><td class=\"left\">Username:</td><td class=\"right\"><input type=\"text\" name=\"username\"></td></tr>"
	print "<tr><td class=\"left\">Privileges:</td><td class=\"right\"><div>"
	for name, desc in privList.iteritems():
		print "<input type=\"checkbox\" name=\"privileges[]\" id=\"priv_%s\" value=\"%s\"%s><label for=\"priv_%s\">%s &mdash; %s</label><br>" % (name, name, ' checked' if name in privDefaults else '', name, name, desc)
	print "</div></td></tr>"
	if settings.smtpServer:
		print "<tr><td class=\"left\">Contact:</td><td class=\"right\"><div><input type=\"checkbox\" name=\"send_welcome\" id=\"send_welcome\" checked><label for=\"send_welcome\">Send welcome e-mail</label></div></td></tr>"
	print "<tr><td class=\"left\">&nbsp;</td><td class=\"right\">"
	print Button('Save', id = 'save-button', type = 'submit').positive()
	print Button('Cancel', type = 'button', url = '/admin').negative()
	print "</td></tr>"
	print "</table>"
	print "</form><br>"

	print "<h3>Current Users</h3>"
	users = User.loadAll(orderby = 'username')
	print "<div class=\"user-list\">"
	for user in users:
		print "<div class=\"user-list-entry\"><a href=\"/users/%s\"><img src=\"%s\"></a><br>%s</div>" % (user.username, user.getAvatar(64), user.safe.username)
	print "</div>"
	print "<div class=\"clear\"></div>"
Exemple #7
0
def adminProjectsManage(handler, id):
	handler.title('Manage Project')
	requirePriv(handler, 'Admin')
	project = Project.load(int(id))
	if not project:
		ErrorBox.die('Invalid Project', "No project with ID <b>%d</b>" % int(id))
	undelay(handler)

	sprints = project.getSprints()
	otherProjects = sorted((p for p in Project.loadAll() if p != project), key = lambda p: p.name)

	for sprint in sprints:
		if 'deleted' in sprint.flags:
			print "<form method=\"post\" action=\"/admin/projects/%d/cancel-deletion/%d\">" % (project.id, sprint.id)
			print WarningBox("%s is flagged for deletion during nightly cleanup. %s" % (sprint.link(handler.session['user']), Button('Cancel').mini().post()))
			print "</form>"

	print "<a name=\"sprints\"></a>"
	print "<h3>Sprints</h3>"
	if len(sprints) > 0:
		print "<form method=\"post\" action=\"/admin/projects/%d/sprints\">" % project.id
		for sprint in sprints:
			print "<input type=\"checkbox\" name=\"sprintid[]\" value=\"%d\">&nbsp;%s<br>" % (sprint.id, sprint.link(handler.session['user']))
		print "<br>"
		print "Move to project: <select name=\"newproject\">"
		for p in otherProjects:
			print "<option value=\"%d\">%s</option>" % (p.id, p.safe.name)
		print "</select>"
		print Button('Move').post('move').positive()
		print "<br><br>"
		print "Delete sprints (irreversible once finalized):"
		print Button('Delete').post('delete').negative()
		print "</form>"
	else:
		print "No sprints"

	print "<a name=\"rename\"></a>"
	print "<h3>Rename</h3>"
	print "<form method=\"post\" action=\"/admin/projects/%d/edit\">" % project.id
	print "Name: <input type=\"text\" name=\"name\" value=\"%s\">" % project.safe.name
	print Button('Rename', type = 'submit').positive()
	print "</form>"

	print "<a name=\"delete\"></a>"
	print "<h3>Delete</h3>"
	print "<form method=\"post\" action=\"/admin/projects/%d/delete\">" % project.id
	if len(project.getSprints()) > 0:
		if len(otherProjects) > 0:
			print "Delete <b>%s</b> and move all sprints to <select name=\"newproject\">" % project.safe.name
			for p in otherProjects:
				print "<option value=\"%d\">%s</option>" % (p.id, p.safe.name)
			print "</select>"
			print Button('Delete', type = 'submit').negative()
		else:
			print "Unable to remove the only project if it has sprints"
	else:
		print Button("Delete %s" % project.safe.name, type = 'submit').negative()
	print "</form><br>"
Exemple #8
0
def sent(handler):
	handler.title('Messages')
	requirePriv(handler, 'User')

	print tabs.where('sent')
	undelay(handler)

	Markdown.head('div.message .body pre code')

	messages = Message.loadAll(senderid = handler.session['user'].id, orderby = '-timestamp')
	for message in messages:
		printMessage(message, False)
Exemple #9
0
def inbox(handler):
	handler.title('Messages')
	requirePriv(handler, 'User')

	print InfoBox("Note", "You can control your automated message subscriptions from the <a href=\"/prefs\">preferences</a> page")

	print tabs.where('inbox')
	undelay(handler)

	Markdown.head('div.message .body pre code')

	messages = Message.loadAll(userid = handler.session['user'].id, orderby = '-timestamp')
	for message in messages:
		printMessage(message, True)
		if not message.read:
			message.read = True
			message.save()
Exemple #10
0
def adminSettings(handler):
	handler.title('Settings')
	requirePriv(handler, 'Admin')
	undelay(handler)

	print "<ul id=\"autolink_icons\" class=\"contextMenu\">"
	for seek in AUTOLINK_ICONS:
		print "<li><a href=\"#%s\"><img src=\"/static/images/%s.png\"></a></li>" % (seek, seek)
	print "</ul>"

	def quot(str): return str.replace('"', '&quot;')

	print "<h3>Mutable settings</h3>"
	print "<form method=\"post\" action=\"/admin/settings\">"
	print "<table class=\"list\"><tbody>"
	print "<tr><td class=\"left\">E-mail domain:</td><td class=\"right\"><input type=\"text\" name=\"emailDomain\" value=\"%s\"></td></tr>" % quot(settings.emailDomain)
	print "<tr><td class=\"left\">System message:</td><td class=\"right\"><input type=\"text\" name=\"systemMessage\" value=\"%s\"></td></tr>" % quot(settings.systemMessage or '')
	print "<tr><td class=\"left\">Redis host:</td><td class=\"right\"><input type=\"text\" name=\"redis\" value=\"%s\"></td></tr>" % quot(settings.redis or '')
	print "<tr><td class=\"left\">Kerberos realm:</td><td class=\"right\"><input type=\"text\" name=\"kerberosRealm\" value=\"%s\"></td></tr>" % quot(settings.kerberosRealm or '')
	print "<tr><td class=\"left\">SMTP server:</td><td class=\"right\"><input type=\"text\" name=\"smtpServer\" value=\"%s\"></td></tr>" % quot(settings.smtpServer or '')
	print "<tr><td class=\"left\">SMTP from address:</td><td class=\"right\"><input type=\"text\" name=\"smtpFrom\" value=\"%s\"></td></tr>" % quot(settings.smtpFrom or '')
	print "<tr><td class=\"left\">Autolinks:</td><td class=\"right\">"
	print "<table class=\"autolinks\" width=\"100%\" cellspacing=\"5\">"
	print "<tr><td>Icon</td><td>Pattern</td><td>URL</td></tr>"
	links = zip(*settings.autolink)
	for i in range(5):
		icon, pattern, url = links.pop(0) if links else ('star', '', '')
		print "<tr>"
		print "<td><img class=\"autolink_icon\" src=\"/static/images/%s.png\"><input type=\"hidden\" name=\"autolink_icons[]\" value=\"%s\"></td>" % (icon, icon)
		print "<td><input type=\"text\" name=\"autolink_patterns[]\" value=\"%s\"></td>" % stripTags(pattern)
		print "<td><input type=\"text\" name=\"autolink_urls[]\" value=\"%s\"></td>" % stripTags(url)
		print "</tr>\n";
	print "</table>"
	print "</td></tr>"
	print "<tr><td class=\"left\">&nbsp;</td><td class=\"right\">"
	print Button('Save', id = 'save-button', type = 'submit').positive()
	print Button('Cancel', type = 'button', url = '/admin').negative()
	print "</td></tr>"
	print "</tbody></table>"
	print "</form>"
	print "<br>"

	print "<h3>Immutable settings</h3>"
	print "<table class=\"list\">"
	print "<tr><td>Database version:</td><td>%s</td></tr>" % settings.dbVersion
	print "</table>"
Exemple #11
0
def adminSessions(handler, username = None):
	handler.title("Sessions for %s" % stripTags(username) if username else "Sessions")
	requirePriv(handler, 'Admin')

	undelay(handler)

	def cmpSessionTimes(s1, s2):
		if 'timestamp' in s1 and 'timestamp' in s2:
			return cmp(s1['timestamp'], s2['timestamp'])
		elif 'timestamp' in s1:
			return 1
		elif 'timestamp' in s2:
			return -1
		else:
			return 0

	print "<table border=0 cellspacing=4>"
	print "<tr><th>Key</th><th>User</th><th>Last address</th><th>Last seen</th><th>&nbsp;</th></tr>"
	sessions = {sessionID: Session.load(sessionID) for sessionID in Session.getIDs()}
	for key, session in sorted(sessions.iteritems(), lambda (k1, s1), (k2, s2): cmpSessionTimes(s1, s2), reverse = True):
Exemple #12
0
def user(handler, username):
	user = User.load(username = username)
	if not user:
		ErrorBox.die('User', "No user named <b>%s</b>" % stripTags(username))

	Markdown.head('form#message-form .body pre code')
	print "<script src=\"/static/jquery.typing-0.2.0.min.js\" type=\"text/javascript\"></script>"
	print "<script src=\"/static/users.js\" type=\"text/javascript\"></script>"
	Chart.include()
	undelay(handler)

	handler.title(user.safe.username)
	handler.replace('$bodytitle$', '', 1)
	print "<img src=\"%s\" class=\"gravatar\">" % user.getAvatar(64)
	print "<h1>%s</h1>" % user.safe.username
	if isDevMode(handler):
		print "<div class=\"debugtext\">User ID: %d</div>" % user.id
	print "<div class=\"clear\"></div>"

	if handler.session['user'] and handler.session['user'].hasPrivilege('Admin'):
		print "<h3>Admin</h3>"
		print "<form method=\"post\" action=\"/admin/users\">"
		print "<input type=\"hidden\" name=\"username\" value=\"%s\">" % user.username
		print "<button type=\"submit\" class=\"btn\" name=\"action\" value=\"resetpw\">Reset password</button>"
		print "<button type=\"submit\" class=\"btn\" name=\"action\" value=\"impersonate\">Impersonate</button>"
		print "<button type=\"submit\" class=\"btn\" name=\"action\" value=\"sessions\">Manage sessions</button>"
		print "<button type=\"submit\" class=\"btn\" name=\"action\" value=\"privileges\">Manage privileges</button>"
		print "</form>"

	if user == handler.session['user']:
		print "<h3>Avatar</h3>"
		if user.hasLocalAvatar():
			print "Your avatar is currently <a href=\"/users/%s/avatar/set\">locally hosted</a>" % user.username
		else:
			print "Your avatar can be changed at <a href=\"http://gravatar.com/\" target=\"_new\">http://gravatar.com/</a>. It must be associated with the e-mail <b>%s</b>, and be rated PG. You can also host an avatar <a href=\"/users/%s/avatar/set\">locally</a>, if necessary" % (user.getEmail(), user.username)

		print "<h3>Authentication</h3>"
		print "Your sprint tool password can be changed <a href=\"/resetpw\">here</a>.",
		if settings.kerberosRealm:
			print "You can also use your %s kerberos password to login" % settings.kerberosRealm,
		print "<br><br>"
		if user.hotpKey == '':
			print "You also have the option to use two-factor authentication via <a href=\"http://en.wikipedia.org/wiki/HOTP\">HOTP</a>. You can use <a href=\"http://support.google.com/a/bin/answer.py?hl=en&answer=1037451\">Google Authenticator</a> to generate verification codes<br><br>"
			print "<form method=\"post\" action=\"/security/two-factor\">"
			print "<button type=\"submit\" class=\"btn danger\" name=\"action\" value=\"enable\">Enable two-factor authentication</button>"
			print "</form>"
		else:
			print "You are currently using two-factor authentication<br><br>"
			print "<form method=\"post\" action=\"/security/two-factor\">"
			print "<button type=\"submit\" class=\"btn danger\" name=\"action\" value=\"enable\">Reset HOTP key</button>"
			print "<button type=\"submit\" class=\"btn danger\" name=\"action\" value=\"disable\">Disable two-factor authentication</button>"
			print "</form>"

		print "<h3>Messages</h3>"
		print "Your inbox and sent messages can be viewed <a href=\"/messages/inbox\">here</a><br>"

	print "<h3>Last seen</h3>"
	if not user.lastseen:
		print "Never"
	elif dateToTs(getNow()) - user.lastseen < 60:
		print "Just now"
	else:
		print "%s ago" % timesince(tsToDate(user.lastseen))

	if handler.session['user'] and handler.session['user'] != user:
		print "<h3>Message</h3>"
		print "<small>(Messages are formatted in <a target=\"_blank\" href=\"/help/markdown\">markdown</a>)</small>"
		print "<form id=\"message-form\" method=\"post\" action=\"/messages/send\">"
		print "<input type=\"hidden\" name=\"userid\" value=\"%d\">" % user.id
		print "<textarea name=\"body\" class=\"large\"></textarea>"
		print "<div class=\"body markdown\"><div id=\"preview\"></div></div>"
		print Button('Send').post().positive()
		print "</form>"

	print "<h3>Project distribution</h3>"
	sprints = filter(lambda s: user in s.members, Sprint.loadAllActive())
	sprintHours = map(lambda s: (s, Availability(s).getAllForward(getNow(), user)), sprints)
	projectHours = map(lambda (p, g): (p, sum(hours for sprint, hours in g)), groupby(sprintHours, lambda (s, a): s.project))

	# For now at least, don't show projects with no hours
	projectHours = filter(lambda (p, h): h > 0, projectHours)
	if len(projectHours) > 0:
		chart = Chart('chart')
		chart.title.text = ''
		chart.tooltip.formatter = "function() {return '<b>' + this.point.name + '</b>: ' + this.point.y + '%';}"
		chart.plotOptions.pie.allowPointSelect = True
		chart.plotOptions.pie.cursor = 'pointer'
		chart.plotOptions.pie.dataLabels.enabled = False
		chart.plotOptions.pie.showInLegend = True
		chart.credits.enabled = False
		chart.series = seriesList = []

		series = {
			'type': 'pie',
			'name': '',
			'data': []
		}
		seriesList.append(series)

		total = sum(hours for project, hours in projectHours)
		for project, hours in projectHours:
			series['data'].append([project.name, float("%2.2f" % (100 * hours / total))])

		chart.js()
		chart.placeholder()
	else:
		print "Not a member of any active sprints"
Exemple #13
0
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('"', '&quot;')
	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\">&nbsp;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\">&nbsp;%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\">&nbsp;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> &mdash; 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> &mdash; 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> &mdash; 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)
Exemple #14
0
def task(handler, ids):
    requirePriv(handler, "User")
    Chart.include()

    Markdown.head(".note .text .body pre code")
    print '<script src="/static/jquery.typing-0.2.0.min.js" type="text/javascript"></script>'
    undelay(handler)

    tasks = {}
    if "," not in ids:  # Single ID
        ids = [int(ids)]
        tasks[ids[0]] = Task.load(ids[0])

        def header(task, text, level):
            if level == 1:
                handler.title(text)
            else:
                print "<h%d>%s</h%d>" % (level, text, level)

    else:  # Many IDs
        ids = map(int, uniq(ids.split(",")))
        tasks = {id: Task.load(id) for id in ids}
        handler.title("Task Information")

        if not all(tasks.values()):
            ids = [str(id) for (id, task) in tasks.iteritems() if not task]
            ErrorBox.die(
                "No %s with %s %s"
                % ("task" if len(ids) == 1 else "tasks", "ID" if len(ids) == 1 else "IDs", ", ".join(ids))
            )

        if len(set(task.sprint for task in tasks.values())) == 1:  # All in the same sprint
            print '<small>(<a href="/sprints/%d?search=highlight:%s">Show in backlog view</a>)</small><br><br>' % (
                tasks.values()[0].sprint.id,
                ",".join(map(str, ids)),
            )

        for id in ids:
            print '<a href="#task%d">%s</a><br>' % (id, tasks[id].safe.name)

        def header(task, text, level):
            if level == 1:
                print "<hr>"
                print '<a name="task%d"></a>' % task.id
                print '<a href="#task%d"><h2>%s</h2></a>' % (task.id, text)
            else:
                print "<h%d>%s</h%d>" % (level + 1, text, level + 1)

    for id in ids:
        task = tasks[id]
        if not task or task.sprint.isHidden(handler.session["user"]):
            ErrorBox.die("Tasks", "No task with ID <b>%d</b>" % id)
        elif not task.sprint.canView(handler.session["user"]):
            ErrorBox.die("Private", "You must be a sprint member to view this sprint's tasks")
        revs = task.getRevisions()
        startRev = task.getStartRevision()

        header(task, task.safe.name, 1)

        header(task, "Info", 2)
        print 'Part of <a href="/sprints/%d">%s</a>, <a href="/sprints/%d#group%d">%s</a>' % (
            task.sprintid,
            task.sprint,
            task.sprintid,
            task.groupid,
            task.group,
        ),
        if task.goal:
            print 'to meet the goal&nbsp;&nbsp;<img class="bumpdown" src="/static/images/tag-%s.png">&nbsp;<a href="/sprints/%d?search=goal:%s">%s</a>' % (
                task.goal.color,
                task.sprintid,
                task.goal.color,
                task.goal.safe.name,
            ),
        print "<br>"
        print "Assigned to %s<br>" % ", ".join(map(str, task.assigned))
        print "Last changed %s ago<br><br>" % timesince(tsToDate(task.timestamp))
        hours, total, lbl = task.hours, startRev.hours, "<b>%s</b>" % statuses[task.status].text
        if task.deleted:
            if task.sprint.canEdit(handler.session["user"]):
                print '<form method="post" action="/sprints/%d">' % task.sprint.id
                print '<input type="hidden" name="id" value="%d">' % task.id
                print '<input type="hidden" name="rev_id" value="%d">' % task.revision
                print '<input type="hidden" name="field" value="deleted">'
                print '<input type="hidden" name="value" value="false">'
                print "Deleted (%s)" % Button("undelete", id="undelete").mini().positive()
                print "</form>"
            else:
                print "Deleted"
            print "<br>"
        elif task.status == "complete":
            print ProgressBar(lbl, total - hours, total, zeroDivZero=True, style="progress-current-green")
        elif task.status in ("blocked", "canceled", "deferred", "split"):
            hours = filter(lambda rev: rev.hours > 0, revs)
            hours = hours[-1].hours if len(hours) > 0 else 0
            print ProgressBar(lbl, total - hours, total, zeroDivZero=True, style="progress-current-red")
        else:
            print ProgressBar(lbl, total - hours, total, zeroDivZero=True)

        header(task, "Notes", 2)
        for note in task.getNotes():
            print '<div id="note%d" class="note">' % note.id
            print '<form method="post" action="/tasks/%d/notes/%d/modify">' % (id, note.id)
            print '<div class="avatar"><img src="%s"></div>' % note.user.getAvatar()
            print '<div class="text">'
            print '<div class="title"><a class="timestamp" href="#note%d">%s</a> by <span class="author">%s</span>' % (
                note.id,
                tsToDate(note.timestamp).replace(microsecond=0),
                note.user.safe.username,
            )
            if note.user == handler.session["user"]:
                print '<button name="action" value="delete" class="fancy mini danger">delete</button>'
            print "</div>"
            print '<div class="body markdown">%s</div>' % note.render()
            print "</div>"
            print "</form>"
            print "</div>"

        print '<div class="note new-note">'
        print '<form method="post" action="/tasks/%d/notes/new">' % id
        print '<div class="avatar"><div><img src="%s"></div></div>' % handler.session["user"].getAvatar()
        print '<div class="text">'
        print '<div class="title">'
        print "<b>New note</b>"
        print '<a target="_blank" href="/help/markdown" class="fancy mini">help</a>'
        print "</div>"
        print '<div class="body"><textarea name="body" class="large"></textarea></div>'
        print Button("Post").post().positive()
        print "<hr>"
        print '<div class="body markdown"><div id="preview"></div></div>'
        print "</div>"
        print "</form>"
        print "</div>"

        print '<button class="btn start-new-note">Add Note</button>'
        print '<div class="clear"></div>'

        header(task, "History", 2)
        chart = TaskChart("chart%d" % id, task)
        chart.js()

        chart.placeholder()
        showHistory(task, False)
        print "<br>"
Exemple #15
0
def projectsList(handler):
    undelay(handler)

    print '<div class="project-list">'
    print '<div class="buttons">'
    print Button("Calendar", "/projects/calendar")
    print "</div>"
    for project in Project.getAllSorted(handler.session["user"]):
        sprints = filter(lambda sprint: not sprint.isHidden(handler.session["user"]), project.getSprints())
        active, inactive = partition(lambda sprint: sprint.isActive() or sprint.isPlanning(), reversed(sprints))
        activeMembers = set()
        for sprint in active:
            activeMembers |= sprint.members
        isTest = project.id < 0

        classes = ["project-summary"]
        if len(active) > 0:
            classes.append("active")
        if isTest:
            classes.append("test")
        print '<div id="project-summary-%d" class="%s">' % (project.id, " ".join(classes))
        print '<div class="project-name">%s</div>' % project.name
        print '<div class="buttons">'
        print Button("new sprint", "/sprints/new?project=%d" % project.id).mini()
        print Button("export", "/sprints/export?project=%d" % project.id).mini()
        print Button("active", "/sprints/active?project=%d" % project.id).mini()
        if handler.session["user"] and handler.session["user"].hasPrivilege("Admin"):
            print Button("manage", "/admin/projects/%d" % project.id).mini().negative()
        print "</div>"

        print '<div class="project-members %s">' % ("short" if len(inactive) <= 6 or len(active) == 0 else "long")
        members = set(project.getMembers())
        scrummasters = set(sprint.owner for sprint in active)
        for member in sorted(scrummasters):
            print '<div class="member scrummaster">%s</div>' % member.str("scrummaster")
        for member in sorted(activeMembers - scrummasters):
            print '<div class="member active">%s</div>' % member.str("member")
        for member in sorted(members - activeMembers - scrummasters):
            print '<div class="member inactive">%s</div>' % member.str("member")
        print "</div>"

        if sprints:

            def printSprint(sprint):
                if sprint.isActive() or sprint.isPlanning():
                    print '<div class="sprint-active">'
                    print '<div class="sprint-name">%s</div>' % sprint.link(handler.session["user"], "sprint-large")
                    print '<div class="sprint-time">'
                    if sprint.isPlanning():
                        print '<span class="label danger">Planning</span>'
                    elif sprint.isReview():
                        print '<span class="label success">Review</span>'
                    else:
                        day = Weekday.today().date()
                        sprintDays = [i.date() for i in sprint.getDays()]
                        print '<span class="label primary">Day %d of %d</span>' % (
                            sprintDays.index(day) + 1,
                            len(sprintDays),
                        )
                    print "</div><br>"
                    for tab in sprintTabs().group("").values():
                        print '<a href="%s">%s</a>' % (tab.getPath(sprint.id), tab.getDisplayName().lower())
                    if handler.session["user"] in sprint.members:
                        print '<a href="/sprints/%d?search=assigned:me">your tasks</a>' % sprint.id
                    print "</div>"
                else:
                    print '<div class="sprint-summary">%s <span class="sprint-time">(%s - %s)</span></div>' % (
                        sprint.link(handler.session["user"]),
                        sprint.getStartStr(),
                        sprint.getEndStr(),
                    )

            print '<div class="sprint-summaries">'
            map(printSprint, active)
            if len(inactive) <= 6:
                map(printSprint, inactive)
            else:
                map(printSprint, inactive[:5])
                print '<div class="show-old-sprints">(%d more)</div>' % (len(inactive) - 5)
                print '<div class="old-sprints">'
                map(printSprint, inactive[5:])
                print "</div>"
            print "</div>"
        else:
            print "&nbsp;"

        print '<div class="clear"></div>'
        print "</div>"
    print "</div>"