--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -2,31 +2,26 @@
import logging
import json, urllib, re
from datetime import datetime, timedelta
-from urllib import urlencode, unquote
+from urllib import urlencode
from webob import exc
# Non-stdlib imports
import pkg_resources
-import tg
-from tg import tmpl_context
from tg import expose, validate, redirect, flash
-from tg import request, response
from tg.decorators import with_trailing_slash, without_trailing_slash
-from pylons import g, c, request
+from pylons import g, c, request, response
from formencode import validators
from pymongo.bson import ObjectId
-from ming.orm.base import session
from ming.orm.ormsession import ThreadLocalORMSession
# Pyforge-specific imports
-from allura.app import Application, ConfigOption, SitemapEntry, DefaultAdminController
+from allura import model as M
from allura.lib import helpers as h
+from allura.app import Application, SitemapEntry, DefaultAdminController
from allura.lib.search import search_artifact
from allura.lib.decorators import audit, react
from allura.lib.security import require, has_artifact_access
-from allura.model import ProjectRole, TagEvent, UserTags, ArtifactReference, Feed, User
-from allura.model import Mailbox, Post
from allura.lib import widgets as w
from allura.lib.widgets import form_fields as ffw
from allura.lib.widgets.subscriptions import SubscribeForm
@@ -35,7 +30,7 @@
from allura.controllers import BaseController
# Local imports
-from forgetracker import model
+from forgetracker import model as TM
from forgetracker import version
from forgetracker.widgets.ticket_form import TicketForm, EditTicketForm
@@ -74,7 +69,7 @@
def __init__(self, project, config):
Application.__init__(self, project, config)
- self.globals = model.Globals.query.get(app_config_id=config._id)
+ self.globals = TM.Globals.query.get(app_config_id=config._id)
self.root = RootController()
self.api_root = RootRestController()
self.admin = TrackerAdminController(self)
@@ -89,7 +84,7 @@
log.info('Headers are: %s', data['headers'])
try:
ticket_num = routing_key.split('.')[-1]
- t = model.Ticket.query.get(ticket_num=int(ticket_num))
+ t = TM.Ticket.query.get(ticket_num=int(ticket_num))
except:
log.exception('Unexpected error routing tkt msg: %s', routing_key)
return
@@ -124,13 +119,13 @@
search_bins = []
related_urls = []
ticket = request.path_info.split(self.url)[-1].split('/')[0]
- for bin in model.Bin.query.find(dict(app_config_id=self.config._id)).sort('summary'):
+ for bin in TM.Bin.query.find(dict(app_config_id=self.config._id)).sort('summary'):
label = bin.shorthand_id()
search_bins.append(SitemapEntry(
h.text.truncate(label, 72), bin.url(), className='nav_child',
small=c.app.globals.bin_counts.get(bin.shorthand_id())))
if ticket.isdigit():
- ticket = model.Ticket.query.find(dict(app_config_id=self.config._id,ticket_num=int(ticket))).first()
+ ticket = TM.Ticket.query.find(dict(app_config_id=self.config._id,ticket_num=int(ticket))).first()
else:
ticket = None
links = [
@@ -138,21 +133,21 @@
SitemapEntry('View Stats', self.config.url() + 'stats', ui_icon='folder-collapsed')]
if ticket:
for aref in ticket.references+ticket.backreferences.values():
- artifact = ArtifactReference(aref).artifact
+ artifact = M.ArtifactReference(aref).artifact
if artifact is None: continue
- artifact = artifact.primary(model.Ticket)
+ artifact = artifact.primary(TM.Ticket)
if artifact.url() not in related_urls:
related_urls.append(artifact.url())
title = '%s: %s' % (artifact.type_s, artifact.shorthand_id())
related_artifacts.append(SitemapEntry(title, artifact.url(), className='nav_child'))
if ticket.super_id:
links.append(SitemapEntry('Supertask'))
- super = model.Ticket.query.get(_id=ticket.super_id, app_config_id=c.app.config._id)
+ super = TM.Ticket.query.get(_id=ticket.super_id, app_config_id=c.app.config._id)
links.append(SitemapEntry('[#{0}]'.format(super.ticket_num), super.url(), className='nav_child'))
if ticket.sub_ids:
links.append(SitemapEntry('Subtasks'))
for sub_id in ticket.sub_ids or []:
- sub = model.Ticket.query.get(_id=sub_id, app_config_id=c.app.config._id)
+ sub = TM.Ticket.query.get(_id=sub_id, app_config_id=c.app.config._id)
links.append(SitemapEntry('[#{0}]'.format(sub.ticket_num), sub.url(), className='nav_child'))
#links.append(SitemapEntry('Create New Subtask', '{0}new/?super_id={1}'.format(self.config.url(), ticket._id), className='nav_child'))
if len(search_bins):
@@ -175,9 +170,9 @@
super(ForgeTrackerApp, self).install(project)
# Setup permissions
- role_developer = ProjectRole.query.get(name='Developer')._id
- role_auth = ProjectRole.query.get(name='*authenticated')._id
- role_anon = ProjectRole.query.get(name='*anonymous')._id
+ role_developer = M.ProjectRole.query.get(name='Developer')._id
+ role_auth = M.ProjectRole.query.get(name='*authenticated')._id
+ role_anon = M.ProjectRole.query.get(name='*anonymous')._id
self.config.acl.update(
configure=c.project.acl['tool'],
read=c.project.acl['read'],
@@ -187,29 +182,29 @@
moderate=[role_developer],
save_searches=[role_developer],
admin=c.project.acl['tool'])
- self.globals = model.Globals(app_config_id=c.app.config._id,
+ self.globals = TM.Globals(app_config_id=c.app.config._id,
last_ticket_num=0,
open_status_names='open unread accepted pending',
closed_status_names='closed wont-fix',
milestone_names='',
custom_fields=[])
c.app.globals.invalidate_bin_counts()
- bin = model.Bin(summary='Open Tickets', terms=self.globals.not_closed_query)
+ bin = TM.Bin(summary='Open Tickets', terms=self.globals.not_closed_query)
bin.app_config_id = self.config._id
bin.custom_fields = dict()
- bin = model.Bin(summary='Recent Changes', terms=self.globals.not_closed_query, sort='mod_date_dt desc')
+ bin = TM.Bin(summary='Recent Changes', terms=self.globals.not_closed_query, sort='mod_date_dt desc')
bin.app_config_id = self.config._id
bin.custom_fields = dict()
def uninstall(self, project):
"Remove all the tool's artifacts from the database"
- model.Attachment.query.remove({'metadata.app_config_id':c.app.config._id})
+ TM.TicketAttachment.query.remove({'metadata.app_config_id':c.app.config._id})
app_config_id = {'app_config_id':c.app.config._id}
- model.Ticket.query.remove(app_config_id)
- model.Bin.query.remove(app_config_id)
+ TM.Ticket.query.remove(app_config_id)
+ TM.Bin.query.remove(app_config_id)
# model.Comment.query.remove(app_config_id)
- model.Globals.query.remove(app_config_id)
+ TM.Globals.query.remove(app_config_id)
super(ForgeTrackerApp, self).uninstall(project)
class RootController(BaseController):
@@ -246,7 +241,12 @@
if 'ticket_num_i' not in refined_sort:
refined_sort += ',ticket_num_i asc'
try:
- matches = search_artifact(model.Ticket, q, rows=limit, sort=refined_sort, start=start, fl='ticket_num_i', **kw) if q else None
+ if q:
+ matches = search_artifact(
+ TM.Ticket, q,
+ rows=limit, sort=refined_sort, start=start, fl='ticket_num_i', **kw)
+ else:
+ matches = None
solr_error = None
except ValueError, e:
solr_error = e.args[0]
@@ -256,7 +256,7 @@
# ticket_numbers is in sorted order
ticket_numbers = [match['ticket_num_i'] for match in matches.docs]
# but query, unfortunately, returns results in arbitrary order
- query = model.Ticket.query.find(dict(app_config_id=c.app.config._id, ticket_num={'$in':ticket_numbers}))
+ query = TM.Ticket.query.find(dict(app_config_id=c.app.config._id, ticket_num={'$in':ticket_numbers}))
# so stick all the results in a dictionary...
ticket_for_num = {}
for t in query:
@@ -275,7 +275,7 @@
require(has_artifact_access('read'))
result = self.paged_query(c.app.globals.not_closed_query, sort='ticket_num_i desc', limit=int(limit))
c.subscribe_form = W.subscribe_form
- result['subscribed'] = Mailbox.subscribed()
+ result['subscribed'] = M.Mailbox.subscribed()
c.ticket_search_results = W.ticket_search_results
return result
@@ -338,7 +338,7 @@
else:
feed_type = 'rss'
title = 'Recent changes to %s' % c.app.config.options.mount_point
- feed = Feed.feed(
+ feed = M.Feed.feed(
{'artifact_reference.mount_point':c.app.config.options.mount_point,
'artifact_reference.project_id':c.project._id},
feed_type,
@@ -361,18 +361,18 @@
c.app.globals.milestone_names = ''
ticket_num = ticket_form.pop('ticket_num', None)
if ticket_num:
- ticket = model.Ticket.query.get(
+ ticket = TM.Ticket.query.get(
app_config_id=c.app.config._id,
ticket_num=ticket_num)
if not ticket:
raise Exception('Ticket number not found.')
else:
- ticket = model.Ticket(
+ ticket = TM.Ticket(
app_config_id=c.app.config._id,
custom_fields=dict(),
ticket_num=c.app.globals.next_ticket_num())
ticket.update(ticket_form)
- for u in ProjectRole.query.find({'name':'Admin'}).first().users_with_role():
+ for u in M.ProjectRole.query.find({'name':'Admin'}).first().users_with_role():
ticket.subscribe(user=u)
redirect(str(ticket.ticket_num)+'/')
@@ -395,7 +395,7 @@
@expose()
def update_tickets(self, **post_data):
c.app.globals.invalidate_bin_counts()
- tickets = model.Ticket.query.find(dict(
+ tickets = TM.Ticket.query.find(dict(
_id={'$in':[ObjectId(id) for id in post_data['selected'].split(',')]},
app_config_id=c.app.config._id)).all()
for ticket in tickets:
@@ -439,10 +439,10 @@
def tickets_since(self, when=None):
count = 0
if when:
- count = model.Ticket.query.find(dict(app_config_id=c.app.config._id,
+ count = TM.Ticket.query.find(dict(app_config_id=c.app.config._id,
created_date={'$gte':when})).count()
else:
- count = model.Ticket.query.find(dict(app_config_id=c.app.config._id)).count()
+ count = TM.Ticket.query.find(dict(app_config_id=c.app.config._id)).count()
return count
def ticket_comments_since(self, when=None):
@@ -450,16 +450,16 @@
discussion_id=c.app.config.discussion_id)
if when is not None:
q['timestamp'] = {'$gte':when}
- return Post.query.find(q).count()
+ return M.Post.query.find(q).count()
@with_trailing_slash
@expose('jinja:tracker/stats.html')
def stats(self):
require(has_artifact_access('read'))
globals = c.app.globals
- total = model.Ticket.query.find(dict(app_config_id=c.app.config._id)).count()
- open = model.Ticket.query.find(dict(app_config_id=c.app.config._id,status={'$in': list(globals.set_of_open_status_names)})).count()
- closed = model.Ticket.query.find(dict(app_config_id=c.app.config._id,status={'$in': list(globals.set_of_closed_status_names)})).count()
+ total = TM.Ticket.query.find(dict(app_config_id=c.app.config._id)).count()
+ open = TM.Ticket.query.find(dict(app_config_id=c.app.config._id,status={'$in': list(globals.set_of_open_status_names)})).count()
+ closed = TM.Ticket.query.find(dict(app_config_id=c.app.config._id,status={'$in': list(globals.set_of_closed_status_names)})).count()
now = datetime.utcnow()
week = timedelta(weeks=1)
fortnight = timedelta(weeks=2)
@@ -497,9 +497,9 @@
def subscribe(self, subscribe=None, unsubscribe=None):
require(has_artifact_access('read'))
if subscribe:
- Mailbox.subscribe(type='direct')
+ M.Mailbox.subscribe(type='direct')
elif unsubscribe:
- Mailbox.unsubscribe()
+ M.Mailbox.unsubscribe()
redirect(request.referer)
class BinController(BaseController):
@@ -515,7 +515,7 @@
def index(self, **kw):
require(has_artifact_access('save_searches', app=self.app))
c.bin_form = W.bin_form
- bins = model.Bin.query.find()
+ bins = TM.Bin.query.find()
count=0
count = len(bins)
return dict(bins=bins or [], count=count, app=self.app)
@@ -525,7 +525,7 @@
def bins(self):
require(has_artifact_access('save_searches', app=self.app))
c.bin_form = W.bin_form
- bins = model.Bin.query.find()
+ bins = TM.Bin.query.find()
count=0
count = len(bins)
return dict(bins=bins or [], count=count, app=self.app)
@@ -548,8 +548,8 @@
if request.method != 'POST':
raise Exception('save_bin must be a POST request')
if bin_form['old_summary']:
- model.Bin.query.find(dict(summary=bin_form['old_summary'])).first().delete()
- bin = model.Bin(summary=bin_form['summary'], terms=bin_form['terms'])
+ TM.Bin.query.find(dict(summary=bin_form['old_summary'])).first().delete()
+ bin = TM.Bin(summary=bin_form['summary'], terms=bin_form['terms'])
bin.app_config_id = self.app.config._id
bin.custom_fields = dict()
redirect('.')
@@ -557,7 +557,7 @@
@with_trailing_slash
@expose()
def delbin(self, summary=None):
- bin = model.Bin.query.find(dict(summary=summary,)).first()
+ bin = TM.Bin.query.find(dict(summary=summary,)).first()
require(has_artifact_access('save_searches', app=self.app))
self.app.globals.invalidate_bin_counts()
bin.delete()
@@ -619,7 +619,7 @@
def __init__(self, ticket_num=None):
if ticket_num is not None:
self.ticket_num = int(ticket_num)
- self.ticket = model.Ticket.query.get(app_config_id=c.app.config._id,
+ self.ticket = TM.Ticket.query.get(app_config_id=c.app.config._id,
ticket_num=self.ticket_num)
self.attachment = AttachmentsController(self.ticket)
# self.comments = CommentController(self.ticket)
@@ -646,10 +646,10 @@
if c.app.globals.milestone_names is None:
c.app.globals.milestone_names = ''
thread = self.ticket.discussion_thread
- post_count = Post.query.find(dict(discussion_id=thread.discussion_id, thread_id=thread._id)).count()
+ post_count = M.Post.query.find(dict(discussion_id=thread.discussion_id, thread_id=thread._id)).count()
return dict(ticket=self.ticket, globals=c.app.globals,
allow_edit=has_artifact_access('write', self.ticket)(),
- subscribed=Mailbox.subscribed(artifact=self.ticket),
+ subscribed=M.Mailbox.subscribed(artifact=self.ticket),
page=page, limit=limit, count=post_count)
else:
raise exc.HTTPNotFound, 'Ticket #%s does not exist.' % self.ticket_num
@@ -665,7 +665,7 @@
if c.app.globals.milestone_names is None:
c.app.globals.milestone_names = ''
return dict(ticket=self.ticket, globals=c.app.globals,
- subscribed=Mailbox.subscribed(artifact=self.ticket))
+ subscribed=M.Mailbox.subscribed(artifact=self.ticket))
@without_trailing_slash
@expose()
@@ -682,7 +682,7 @@
feed_type = 'rss'
title = 'Recent changes to %d: %s' % (
self.ticket.ticket_num, self.ticket.summary)
- feed = Feed.feed(
+ feed = M.Feed.feed(
{'artifact_reference':self.ticket.dump_ref()},
feed_type,
title,
@@ -754,7 +754,10 @@
c.app.globals.milestone_names = ''
if 'attachment' in post_data and post_data['attachment']:
for attachment in post_data['attachment']:
- self.ticket.attach(file_info=attachment)
+ if not hasattr(attachment, 'file'): continue
+ TM.TicketAttachment.save_attachment(
+ attachment.filename, attachment.file, content_type=attachment.type,
+ ticket_id=self.ticket._id)
any_sums = False
for cf in c.app.globals.custom_fields or []:
if 'custom_fields.'+cf.name in post_data:
@@ -809,7 +812,7 @@
redirect(request.referer)
class AttachmentController(ac.AttachmentController):
- AttachmentClass = model.Attachment
+ AttachmentClass = TM.TicketAttachment
edit_perm = 'write'
class AttachmentsController(ac.AttachmentsController):
@@ -869,7 +872,7 @@
require(has_artifact_access('read'))
return dict(tickets=[
dict(ticket_num=t.ticket_num, summary=t.summary)
- for t in model.Ticket.query.find(dict(app_config_id=c.app.config._id)).sort('ticket_num') ])
+ for t in TM.Ticket.query.find(dict(app_config_id=c.app.config._id)).sort('ticket_num') ])
@expose()
@h.vardec
@@ -881,7 +884,7 @@
raise Exception('save_ticket must be a POST request')
if c.app.globals.milestone_names is None:
c.app.globals.milestone_names = ''
- ticket = model.Ticket(
+ ticket = TM.Ticket(
app_config_id=c.app.config._id,
custom_fields=dict(),
ticket_num=c.app.globals.next_ticket_num())
@@ -898,7 +901,7 @@
def __init__(self, ticket_num):
if ticket_num is not None:
self.ticket_num = int(ticket_num)
- self.ticket = model.Ticket.query.get(app_config_id=c.app.config._id,
+ self.ticket = TM.Ticket.query.get(app_config_id=c.app.config._id,
ticket_num=self.ticket_num)
@expose('json:')