예제 #1
0
파일: users.py 프로젝트: mrozekma/Sprint
def userAvatarSet(handler, username):
	handler.title('Set avatar')
	requirePriv(handler, 'User')
	user = User.load(username = username)
	if not user:
		ErrorBox.die("Set avatar", "No user named <b>%s</b>" % stripTags(username))
	if user != handler.session['user']: #TODO Allow devs
		redirect("/users/%s/avatar/set" % handler.session['user'].username)
	if not Image:
		ErrorBox.die("Set avatar", "This sprint install does not have the Python Imaging Library (PIL), so local avatars are not supported")

	print "Restrictions on a locally hosted avatar:"
	print "<ul>"
	print "<li>Type: %s</li>" % ", ".join(AVATAR_TYPES).upper()
	print "<li>Size: %s bytes</li>" % AVATAR_MAX_SIZE
	print "</ul>"

	print "<form method=\"post\" enctype=\"multipart/form-data\" action=\"/users/%s/avatar/set\">" % user.username
	print "<input type=\"file\" name=\"data\"><br>"
	# Using a plain button here because the file field isn't styled
	print "<button>Upload</button>"
	print "</form>"

	if user.hasLocalAvatar():
		print "<br>"
		print "You can also remove your existing local avatar. Your account will switch back to using your gravatar image<br>"
		print "<form method=\"post\" action=\"/users/%s/avatar/remove\">" % user.username
		print "<button>Remove avatar</button>"
		print "</form>"
예제 #2
0
파일: admin.py 프로젝트: mrozekma/Sprint
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)
예제 #3
0
파일: tasks.py 프로젝트: nolandda/Sprint
def newNoteModify(handler, taskid, id, p_action):
    handler.title("New Note")
    requirePriv(handler, "User")

    if p_action != "delete":
        ErrorBox.die("Invalid Action", "Unrecognized action <b>%s</b>" % p_action)

    taskid = int(taskid)
    task = Task.load(taskid)
    if not task or task.sprint.isHidden(handler.session["user"]):
        ErrorBox.die("Invalid Task", "No task with ID <b>%d</b>" % taskid)

    id = int(id)
    note = Note.load(id)
    if not note:
        ErrorBox.die("Invalid Note", "No note with ID <b>%d</b>" % noteid)
    elif note.task != task:  # Doesn't really matter, but shouldn't happen
        ErrorBox.die("Task mismatch", "Note/task mismatch")
    elif note.user != handler.session["user"]:
        ErrorBox.die("Permission denied", "Notes can only be deleted by their creators")

    note.delete()
    delay(handler, SuccessBox("Deleted note", close=3))
    Event.deleteNote(handler, note)
    redirect("/tasks/%d" % task.id)
예제 #4
0
파일: search.py 프로젝트: mrozekma/Sprint
def updateSavedSearch(handler, id, p_action, p_name, p_query, p_share = False):
	handler.title('Update Search')
	requirePriv(handler, 'User')

	search = SavedSearch.load(int(id))
	if not search:
		ErrorBox.die('Invalid Search', "No search with ID <b>%d</b>" % int(id))
	elif search.user != handler.session['user']:
		ErrorBox.die('Permission Denied', "You cannot modify another user's search")

	if p_action == 'update':
		search.name = p_name
		search.query = p_query
		search.public = p_share
		search.save()
		if not search.public:
			search.unfollow()
		delay(handler, SuccessBox("Updated search <b>%s</b>" % search.safe.name))
	elif p_action == 'delete':
		search.delete()
		delay(handler, SuccessBox("Deleted search <b>%s</b>" % search.safe.name))
	else:
		ErrorBox.die('Invalid Action', "Unknown action %s" % stripTags(p_action))

	redirect('/search/saved')
예제 #5
0
파일: groups.py 프로젝트: mrozekma/Sprint
def newGroupPost(handler, p_group, p_name):
	def die(msg):
		print msg
		done()

	handler.title('New Group')
	requirePriv(handler, 'User')
	handler.wrappers = False

	predid = to_int(p_group, 'group', die)
	pred = Group.load(predid)
	if not pred:
		die("No group with ID <b>%d</b>" % predid)
	elif p_name.strip() == '':
		die("Group must have a non-empty name")

	group = Group(pred.sprint.id, p_name, pred.seq + 1)
	group.save()

	handler.responseCode = 299
	delay(handler, """
<script type=\"text/javascript\">
$(document).ready(function() {
	$('#group%d').effect('highlight', {}, 3000);
});
</script>""" % group.id)
	print "/sprints/%d#group%d" % (group.sprint.id, group.id)
	Event.newGroup(handler, group)
예제 #6
0
파일: sprints.py 프로젝트: mrozekma/Sprint
def findActiveSprint(handler, project = None, search = None):
	handler.title('Active Sprint')
	requirePriv(handler, 'User')
	if project:
		projectid = int(project)
		project = Project.load(projectid)
		if not project:
			ErrorBox.die('Load project', "No project with ID <b>%d</b>" % projectid)

	url = "/sprints/%d"
	if search:
		url += "?search=%s" % search

	sprints = Sprint.loadAllActive(handler.session['user'], project)
	sprints = filter(lambda sprint: sprint.canView(handler.session['user']), sprints)
	for case in switch(len(sprints)):
		if case(0):
			ErrorBox.die('Active sprint', 'No active sprints found')
			break
		if case(1):
			redirect(url % sprints[0].id)
			break
		if case():
			print "You are active in multiple sprints%s:<br><br>" % (" in the %s project" % project.safe.name if project else '')
			for sprint in sprints:
				print "<a href=\"%s\">%s</a><br>" % (url % sprint.id, sprint.safe.name)
			break
예제 #7
0
파일: search.py 프로젝트: mrozekma/Sprint
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>"
예제 #8
0
파일: admin.py 프로젝트: mrozekma/Sprint
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>"
예제 #9
0
파일: search.py 프로젝트: mrozekma/Sprint
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()
예제 #10
0
파일: security.py 프로젝트: nolandda/Sprint
def twoFactorAuthentication(handler, p_action):
    handler.title("Two-Factor Authentication")
    requirePriv(handler, "User")

    if p_action == "enable":
        handler.session["user"].hotpKey = b32encode("".join(chr(randrange(256)) for _ in range(10)))
        handler.session["user"].save()
        Event.tfa(handler, handler.session["user"])

        print SuccessBox("Two-factor authentication is <b>enabled</b>")
        print "Your HOTP key is <b>%s</b>:<br><br>" % handler.session["user"].hotpKey
        print '<div style="text-align: center"><img src="https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/Sprint%%3Fsecret%%3D%s"></div><br>' % handler.session[
            "user"
        ].hotpKey

        print "This key will not be displayed again &mdash; be sure to write it down, or add it to your Google Authenticator list now"
    elif p_action == "disable":
        handler.session["user"].hotpKey = ""
        handler.session["user"].save()
        Event.tfa(handler, handler.session["user"])

        delay(handler, SuccessBox("Two-factor authentication is <b>disabled</b>"))
        redirect("/users/%s" % handler.session["user"].username)
    else:
        ErrorBox.die("Unexpected action: <b>%s</b>" % stripTags(p_action))
예제 #11
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminProjectsMoveSprintsPost(handler, id, p_newproject, p_sprintid = None):
	handler.title('Move Sprints')
	requirePriv(handler, 'Admin')
	project = Project.load(int(id))
	if not project:
		ErrorBox.die('Invalid Project', "No project with ID <b>%d</b>" % int(id))

	p_newproject = to_int(p_newproject, 'newproject', ErrorBox.die)
	target = Project.load(p_newproject)

	if not p_sprintid:
		delay(handler, WarningBox("No sprints to move", close = True))
		redirect("/admin/projects/%d" % project.id)

	sprintids = [to_int(id, 'sprintid', ErrorBox.die) for id in p_sprintid]
	sprints = [Sprint.load(id) for id in sprintids]
	if not all(sprints):
		ErrorBox.die("Invalid sprint ID(s)")

	for sprint in sprints:
		sprint.project = target
		sprint.save()

	delay(handler, SuccessBox("%s moved" % pluralize(len(sprints), 'sprint', 'sprints'), close = True))
	redirect("/admin/projects/%d" % project.id)
예제 #12
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminTime(handler):
	handler.title('Mock time')
	requirePriv(handler, 'Admin')
	print "<link href=\"/static/jquery.ui.timepicker.css\" rel=\"stylesheet\" type=\"text/css\" />"
	print "<script src=\"/static/jquery.ui.timepicker.js\" type=\"text/javascript\"></script>"
	print "<script src=\"/static/admin-time.js\" type=\"text/javascript\"></script>"

	nowDelta = getNowDelta()
	real, effective = datetime.now(), getNow()
	days = nowDelta.days
	if nowDelta.seconds < 0:
		prefix = '-'
		times = tsToDate(0) + timedelta(hours = 24 - datetime.fromtimestamp(0).hour) - nowDelta
	else:
		prefix = ''
		times = tsToDate(0) + timedelta(hours = 24 - datetime.fromtimestamp(0).hour) + nowDelta

	tbl = LRTable()
	tbl['Real time:'] = str(real)
	tbl['Current delta:'] = "%d %s, %s%d:%02d" % (days, 'day' if days == 1 else 'days', prefix, times.hour, times.minute)
	tbl['Effective time:'] = str(effective)
	print tbl

	print "<br>"
	print "<form method=\"post\" action=\"/admin/time\">"
	print "<input type=\"text\" name=\"date\" class=\"date\" value=\"%s\">" % effective.strftime('%m/%d/%Y')
	print "<input type=\"text\" name=\"time\" class=\"time\" value=\"%s\">" % effective.strftime('%H:%M')
	print Button('Set', type = 'submit').positive()
	print "</form>"
예제 #13
0
파일: sprints.py 프로젝트: mrozekma/Sprint
def exportSprints(handler, project):
	id = int(project)
	handler.title('Export Sprint')
	requirePriv(handler, 'User')
	project = Project.load(id)
	if not project:
		ErrorBox.die('Invalid project', "No project with ID <b>%d</b>" % id)

	print "<link href=\"/static/jquery.multiselect.css\" rel=\"stylesheet\" type=\"text/css\" />"
	print "<script src=\"/static/jquery.multiselect.js\" type=\"text/javascript\"></script>"
	print "<script src=\"/static/sprints-export.js\" type=\"text/javascript\"></script>"
	print "<style type=\"text/css\">"
	print "select {width: 50%;}"
	print "img.format {"
	print "    width: 64px;"
	print "    cursor: pointer;"
	print "    padding: 5px;"
	print "    border: 3px solid #fff;"
	print "}"
	print "img.format.selected, .ui-effects-transfer {border: 3px solid #f00;}"
	print "</style>"

	print "<h2>Sprints</h2>"
	print "<select name=\"sprints\" multiple>"
	for sprint in project.getSprints():
		if sprint.canView(handler.session['user']):
			print "<option value=\"%d\"%s>%s</option>" % (sprint.id, ' selected' if sprint.isActive() else '', sprint.safe.name)
	print "</select>"

	print "<h2>Format</h2>"
	for export in exports.values():
		print "<img class=\"format\" src=\"/static/images/%s.png\" title=\"%s\" export-name=\"%s\">" % (export.getIcon(), export.getFriendlyName(), export.getName())

	print "<br><br>"
	print Button('Export', id = 'export-button').positive()
예제 #14
0
파일: admin.py 프로젝트: mrozekma/Sprint
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>"
예제 #15
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminPrivilegesPost(handler, p_grant):
	handler.title("Privileges")
	requirePriv(handler, 'Admin')
	p_grant = {name: privs.keys() for name, privs in p_grant.iteritems()}

	privNames = set()
	for privs in p_grant.values():
		privNames |= set(privs)
	if not privNames <= set(privList.keys()):
		ErrorBox.die("Update privileges", "Unrecognized privilege name")

	for user in User.loadAll():
		for priv in privList:
			privs = p_grant.get(user.username, [])
			has = user.hasPrivilege(priv)
			if has and priv not in privs:
				print "Revoking %s from %s<br>" % (priv, user.username)
				user.privileges.remove(priv)
				Event.revokePrivilege(handler, user, priv)
			elif not has and priv in privs:
				print "Granting %s to %s<br>" % (priv, user.username)
				user.privileges.add(priv)
				Event.grantPrivilege(handler, user, priv, False)
		user.save()
	print "Done"
예제 #16
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminSessionsPost(handler, p_key, p_action, p_value = None):
	handler.title('Sessions')
	requirePriv(handler, 'Admin')
	print "<script src=\"/static/admin-sessions.js\" type=\"text/javascript\"></script>"

	if not p_key in Session.getIDs():
		ErrorBox.die("Retrieve session", "No session exists with key <b>%s</b>" % stripTags(p_key))
	session = Session.load(p_key)

	for case in switch(p_action):
		if case('reassign'):
			handler.title('Reassign Session')
			if p_value:
				user = User.load(int(p_value))
				if not user:
					ErrorBox.die("Load user", "No user exists with ID <b>%s</b>" % stripTags(p_value))
				session['user'] = user
				redirect('/admin/sessions')
			else:
				print "<form method=\"post\" action=\"/admin/sessions\">"
				print "<input type=\"hidden\" name=\"action\" value=\"reassign\">"
				print "<input type=\"hidden\" name=\"key\" value=\"%s\">" % p_key
				print "<select id=\"selectUser\" name=\"value\">"
				for user in sorted(User.loadAll()):
					print "<option value=\"%d\">%s</option>" % (user.id, user.safe.username)
				print "</select><br>"
				print Button('Reassign', type = 'submit').positive()
				print Button('Cancel', id = 'cancel-button', type = 'button', url = '/admin/sessions').negative()
				print "</form>"
				break
		if case('destroy'):
			Session.destroy(p_key)
			redirect('/admin/sessions')
			break
		break
예제 #17
0
파일: sprints.py 프로젝트: mrozekma/Sprint
def showSprintResults(handler, id):
	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")

	tasksNow = sprint.getTasks()
	tasksStart = filter(None, (task.getRevisionAt(tsToDate(sprint.start)) for task in tasksNow))
	tasksNow = {task.id: task for task in tasksNow}
	tasksStart = {task.id: task for task in tasksStart}

	handler.title(sprint.safe.name)
	drawNavArrows(sprint, handler.session['user'], 'results')
	print tabs(sprint, ('wrapup', 'results'))

	if not sprint.isOver():
		ErrorBox.die('Sprint Open', "Results aren't available until the sprint has closed")

	from rorn.Box import WarningBox
	print WarningBox("This feature is still under development")

	print "<ul>"

	completed = filter(lambda task: task.status == 'complete', tasksNow.values())
	print "<li><a href=\"/sprints/%d?search=status:complete\">%s completed</a> (%d%% of the original hourly commitment)</li>" % (sprint.id, pluralize(len(completed), 'task', 'tasks'), sum(tasksStart[task.id].hours if task.id in tasksStart else 0 for task in completed) * 100 / sum(task.hours for task in tasksStart.values()))

	planned = filter(lambda task: task.id in tasksStart, tasksNow.values())
	print "<li>%d%% of tasks were planned from the start</li>" % (len(planned) * 100 / len(tasksNow))

	print "</ul>"
예제 #18
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminInfo(handler):
	handler.title('Information')
	requirePriv(handler, 'Admin')

	print "<div class=\"info\">"

	print "<h3>Uptime</h3>"
	loadTime = getLoadtime()
	print "Started %s<br>" % loadTime
	print "Up for %s<br>" % timesince(loadTime)
	print "Total requests: %d<br>" % server().getTotalRequests()
	print "<form method=\"post\" action=\"/admin/restart\">"
	print Button('Restart', type = 'submit').negative()
	print "</form>"

	print "<h3>Threads</h3>"
	print "<table border=\"1\" cellspacing=\"0\" cellpadding=\"4\">"
	print "<tr><th>ID</th><th class=\"main\">Name</th><th>Alive</th><th>Daemon</th></tr>"
	for thread in sorted(threads(), key = lambda thread: thread.name):
		print "<tr><td>%s</td><td>" % ('None' if thread.ident is None else "%x" % abs(thread.ident))
		print thread.name
		print "<br>"
		try:
			print CollapsibleBox('Traceback', formatTrace(traceback.extract_stack(sys._current_frames()[thread.ident])))
		except Exception:
			pass
		print "</td><td class=\"%s\">&nbsp;</td><td class=\"%s\">&nbsp;</td></tr>" % ('yes' if thread.isAlive() else 'no', 'yes' if thread.daemon else 'no')
	print "</table>"

	print "<h3>Locks</h3>"
	print "<table border=\"1\" cellspacing=\"0\" cellpadding=\"4\">"
	print "<tr><th class=\"main\">Name</th><th>Available</th><th>Reentrant</th></tr>"
	for (name, lock) in sorted(locks.iteritems()):
		print "<tr><td>"
		print name
		avail = lock.avail()
		if not avail:
			print "<br>"
			writer = ResponseWriter()
			try:
				owner, tb = lock.owner, lock.tb
				name = ("%x" % abs(owner)) if owner else 'None'
				#TODO Is there no O(1) way to do this?
				for thread in threads():
					if thread.ident == owner:
						name = "%s (%x)" % (thread.name, abs(owner))
						break
				print "Owned by: <b>%s</b><br><br>" % name
				if tb:
					print "Acquisition traceback:<br>"
					print formatTrace(tb)
					print "<br>"
				print "Current traceback:<br>"
				print formatTrace(traceback.extract_stack(sys._current_frames()[owner]))
			except Exception, e:
				writer.clear()
				print "<i>(Unable to retrieve stack trace)</i>"
			print CollapsibleBox('Ownership', writer.done())
		print "</td><td class=\"%s\">%s</td><td class=\"%s\">&nbsp;</td></tr>" % ('yes' if avail else 'no', '&nbsp;' if avail else (lock.owner or '???'), 'yes' if lock.reentrant() else 'no')
예제 #19
0
파일: tasks.py 프로젝트: nolandda/Sprint
def taskEditPost(handler, ids, p_hours, p_status, p_goal, p_assigned=[], p_include={}):
    handler.title("Edit tasks")
    requirePriv(handler, "Write")

    allIDs = map(int, uniq(ids.split(",")))
    ids = map(lambda i: to_int(i, "include", ErrorBox.die), p_include.keys())
    if not set(ids) <= set(allIDs):
        ErrorBox.die("Included tasks don't match query arguments")

    tasks = dict((id, Task.load(id)) for id in ids)
    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))
        )
    tasks = [tasks[id] for id in ids]
    if len(set(task.sprint for task in tasks)) > 1:
        ErrorBox.die("All tasks must be in the same sprint")
    sprint = (tasks[0] if len(tasks) > 0 else Task.load(allIDs[0])).sprint
    if sprint.isHidden(handler.session["user"]):
        ErrorBox.die(
            "No %s with %s %s"
            % ("task" if len(ids) == 1 else "tasks", "ID" if len(ids) == 1 else "IDs", ", ".join(ids))
        )
    if not sprint.canEdit(handler.session["user"]):
        ErrorBox.die("You don't have permission to modify this sprint")

    assignedids = set(to_int(i, "assigned", ErrorBox.die) for i in p_assigned)

    changes = {
        "assigned": False if assignedids == set() else {User.load(assignedid) for assignedid in assignedids},
        "hours": False if p_hours == "" else int(p_hours),
        "status": False if p_status == "" else p_status,
        "goal": False if p_goal == "" else Goal.load(int(p_goal)),
    }

    if changes["assigned"] and not all(changes["assigned"]):
        ErrorBox.die("Invalid assignee")
    if changes["assigned"] and not set(changes["assigned"]).issubset(sprint.members):
        ErrorBox.die("Unable to assign tasks to non-sprint members")
    if changes["goal"] and changes["goal"].sprint != sprint:
        ErrorBox.die("Unable to set goal to a goal outside the sprint")

    changed = set()
    for task in tasks:
        for field, value in changes.iteritems():
            if value is not False and getattr(task, field) != value:
                setattr(task, field, value)
                changed.add(task)
                Event.taskUpdate(handler, task, field, value)

    if len(changed) == 0:
        delay(handler, WarningBox("No changes necessary", close=3, fixed=True))
    else:
        for task in changed:
            task.saveRevision(handler.session["user"])
        delay(handler, SuccessBox("Updated %d %s" % (len(changed), "task" if len(changed) == 1 else "tasks")))
    redirect("/sprints/%d" % sprint.id)
예제 #20
0
파일: tasks.py 프로젝트: nolandda/Sprint
def newTaskGeneric(handler, group, assigned=None):
    handler.title("New Task")
    requirePriv(handler, "User")
    page = handler.session["user"].getPrefs().defaultTasksTab
    url = tabs[page].getPath(to_int(group, "group", ErrorBox.die))
    if assigned:
        url += "&assigned=%s" % assigned
    redirect(url)
예제 #21
0
파일: admin.py 프로젝트: mrozekma/Sprint
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>"
예제 #22
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminCronRun(handler, p_name):
	handler.title('Run cron job')
	requirePriv(handler, 'Admin')

	print "<script type=\"text/javascript\">"
	print "job_name = %s;" % toJS(p_name)
	print "</script>"

	print "<div id=\"output\"></div>"
예제 #23
0
파일: tasks.py 프로젝트: nolandda/Sprint
def newTaskPost(handler, p_group, p_name, p_goal, p_status, p_hours, p_assigned=[]):
    def die(msg):
        print msg
        done()

    requirePriv(handler, "User")
    handler.wrappers = False

    groupid = to_int(p_group, "group", die)
    group = Group.load(groupid)
    if not group or group.sprint.isHidden(handler.session["user"]):
        die("No group with ID <b>%d</b>" % groupid)

    sprint = group.sprint
    if not (sprint.isActive() or sprint.isPlanning()):
        die("Unable to modify inactive sprint")
    elif not sprint.canEdit(handler.session["user"]):
        die("You don't have permission to modify this sprint")

    if p_name.strip() == "":
        die("Task must have a non-empty name")

    assignedids = set(to_int(i, "assigned", die) for i in p_assigned)
    assigned = set(User.load(assignedid) for assignedid in assignedids)
    if assigned == set():
        assigned.add(handler.session["user"] if handler.session["user"] in sprint.members else sprint.owner)
    if not all(assigned):
        die("Invalid assignee")

    goalid = to_int(p_goal, "goal", die)
    if goalid != 0:
        goal = Goal.load(goalid)
        if not goal:
            die("No goal with ID <b>%d</b>" % goalid)
        if goal.sprint != group.sprint:
            die("Goal does not belong to the correct sprint")

    hours = to_int(p_hours, "hours", die)

    task = Task(groupid, group.sprintid, handler.session["user"].id, goalid, p_name, p_status, hours)
    task.assigned |= assigned
    task.save()

    handler.responseCode = 299
    delay(
        handler,
        """
<script type=\"text/javascript\">
$(document).ready(function() {
	$('#task%d').effect('highlight', {}, 3000);
});
</script>"""
        % task.id,
    )
    delay(handler, SuccessBox("Added task <b>%s</b>" % task.safe.name, close=3, fixed=True))
    Event.newTask(handler, task)
예제 #24
0
파일: tasks.py 프로젝트: nolandda/Sprint
def distribute(handler, sprint):
    handler.title("Distribute Tasks")
    sprintid = int(sprint)
    sprint = Sprint.load(sprintid)
    if not sprint or sprint.isHidden(handler.session["user"]):
        ErrorBox.die("Invalid Sprint", "No sprint with ID <b>%d</b>" % id)

    handler.title(sprint.safe.name)
    print sprintTabs(sprint, "distribute")
    requirePriv(handler, "Write")
    if not (sprint.isActive() or sprint.isPlanning()):
        ErrorBox.die("Sprint Closed", "Unable to modify inactive sprint")
    if not sprint.canEdit(handler.session["user"]):
        ErrorBox.die("Permission Denied", "You don't have permission to modify this sprint")

    print '<script type="text/javascript" src="/static/highcharts/js/highcharts.js"></script>'
    print '<script type="text/javascript" src="/static/highcharts/js/highcharts-more.js"></script>'
    print '<script type="text/javascript">'
    print "var sprintid = %d;" % sprint.id
    print "</script>"

    print InfoBox("Loading...", id="post-status", close=True)

    print '<div id="distribution-range">'
    print "Acceptable commitment: <span></span>"
    print "</div>"
    print '<div id="distribution-range-slider"></div>'
    print '<div class="clear"></div>'

    print '<div id="distribution-chart"></div>'

    for col in ("left", "right"):
        print '<div class="distribution %s">' % col
        for user in sorted(sprint.members):
            print '<img class="user-gravatar" src="%s" userid="%d" title="%s">' % (
                user.getAvatar(64),
                user.id,
                user.safe.username,
            )
        if col == "right":
            print '<img class="user-gravatar" src="/static/images/revision-deferred.svg" userid="deferred" title="Deferred tasks">'
        print "<br><br>"

        print '<div class="selected">'
        print '<img style="visibility: hidden" class="user-gravatar" src="%s">' % User.getBlankAvatar(64)
        print '<div class="info">'
        print '<div class="username"></div>'
        print '<div class="hours"></div>'
        print '<div class="task-progress-total"><div class="progress-current" style="visibility: hidden;"></div></div>'
        print "</div>"
        print "</div>"
        print '<div class="clear"></div>'
        print '<div class="tasks"></div>'
        print "</div>"

    print '<div class="clear"></div><br><br>'
예제 #25
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminProjectsEditPost(handler, id, p_name):
	handler.title('Edit 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))
	project.name = p_name
	project.save()
	delay(handler, SuccessBox("Saved project changes", close = True))
	redirect("/admin/projects/%d" % project.id)
예제 #26
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminRestart(handler, now = False):
	handler.title('Restart')
	requirePriv(handler, 'Admin')

	if now:
		# This is a POST, so we've already blocked other requests and it's safe to restart
		Event.restart(handler)
		brick("Restart triggered by %s" % handler.session['user'].username) # This string is checked for in main
		server().stop()
	else:
		print "<img src=\"/static/images/loading.gif\">&nbsp;Restarting..."
예제 #27
0
파일: messages.py 프로젝트: mrozekma/Sprint
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)
예제 #28
0
파일: admin.py 프로젝트: mrozekma/Sprint
def adminProjectsPost(handler, p_name):
	handler.title('Project Management')
	requirePriv(handler, 'Admin')

	if Project.load(name = p_name):
		ErrorBox.die('Add Project', "There is already a project named <b>%s</b>" % stripTags(p_name))

	project = Project(p_name)
	project.save()
	delay(handler, SuccessBox("Added project <b>%s</b>" % stripTags(p_name), close = True))
	Event.newProject(handler, project)
	redirect('/')
예제 #29
0
파일: search.py 프로젝트: mrozekma/Sprint
def searchRunSprint(handler, id, sprintid):
	handler.title('Run Search')
	requirePriv(handler, 'User')
	id = int(id)

	search = SavedSearch.load(id)
	if not search:
		ErrorBox.die('Invalid Search', "Search <b>%d</b> does not exist" % int(id))
	elif search.user != handler.session['user'] and not search.public:
		ErrorBox.die('Invalid Search', "You cannot run search <b>%d</b>" % int(id))

	redirect("/sprints/%s?search=%s" % (sprintid, search.query))
예제 #30
0
파일: users.py 프로젝트: mrozekma/Sprint
def userAvatarRemove(handler, username):
	handler.title('Remove avatar')
	requirePriv(handler, 'User')
	user = User.load(username = username)
	if not user:
		ErrorBox.die("Remove avatar", "No user named <b>%s</b>" % stripTags(username))
	if user != handler.session['user']: #TODO Allow devs
		redirect("/users/%s/avatar/set" % handler.session['user'].username)

	user.avatar = None
	user.save()
	delay(handler, SuccessBox("Avatar removed"))
	redirect("/users/%s" % user.username)