summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 211fa28)
raw | patch | inline | side by side (parent: 211fa28)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Fri, 26 Mar 2004 04:50:51 +0000 (04:50 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Fri, 26 Mar 2004 04:50:51 +0000 (04:50 +0000) |
- EditAction was confused about who "self" was
- Edit collision detection was broken for index-page edits
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2204 57a73879-2fb5-44c3-a270-3262357dd7e2
- Edit collision detection was broken for index-page edits
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2204 57a73879-2fb5-44c3-a270-3262357dd7e2
diff --git a/CHANGES.txt b/CHANGES.txt
index b00dbb9c70dd08e556c0913377aee4acd9922a90..952d337e62e659167e1fd89131ed8f1303c613c2 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
- added Reject exception which may be raised by auditors. This is trapped
by mailgw and may be used to veto creation of file attachments or
messages. (sf bug 700265)
+- queries on a per-user basis, and public queries (sf "bug" 891798 :)
Fixed:
- Boolean HTML templating was broken
- Link HTML templating field() was broken
- Fix reporting of test inclusion in postgresql test
+- EditAction was confused about who "self" was
+- Edit collision detection was broken for index-page edits
2004-03-24 0.7.0b1
diff --git a/doc/upgrading.txt b/doc/upgrading.txt
index dbd01ad2cb40363c7ed751a85cf86cf3ed1fed8f..ec390de2ceae25a9d0e336fd43222f0cc8a02029 100644 (file)
--- a/doc/upgrading.txt
+++ b/doc/upgrading.txt
.. contents::
-
-Migrating from 0.7 to 0.8
+Migrating from 0.6 to 0.7
=========================
-0.8.0 Added Dispatcher role
----------------------------
+0.7.0 Saving and sharing of user queries
+----------------------------------------
-A new config option has been added. There is a 'role' that can be filled,
-that of the 'dispatcher'. This person acts as a central sentinel for issues
-coming into the system. You can configure it so that all e-mail error messages
-get bounced to them, them and the user in question, or just the user (default).
+Due to popular demand, the user query saving mechanisms have been
+overhauled. This means that queries remember the user that created them
+and they may be marked as being private for a particular user.
-To toggle these switches, look at the new classic and minimal config.py's,
-specifically:
+You *are not required* to make these changes. You only need to make them
+if you wish to use the new query editing features. It's highly
+recommended, as the effort is minimal.
+1. You will need to edit your tracker's ``dbinit.py`` to change the way
+ queries are stored. Change the lines::
-# The 'dispatcher' is a role that can get notified of new items to the database.
-DISPATCHER_EMAIL = ADMIN_EMAIL
+ query = Class(db, "query",
+ klass=String(), name=String(),
+ url=String())
+ query.setkey("name")
-...
+ to::
+ query = Class(db, "query",
+ klass=String(), name=String(),
+ url=String(), private_for=Link('user'))
-# Send error messages to the dispatcher, user, or both?
-# If 'dispatcher', error message notifications will only be sent to the dispatcher.
-# If 'user', error message notifications will only be sent to the user.
-# If 'both', error message notifications will be sent to both individuals.
-ERROR_MESSAGES_TO = 'user'
+ That is, add the "private_for" property, and remove the line that says
+ ``query.setkey("name")``. The latter is the most important edit here.
+
+2. You will also need to copy the ``query.edit.html`` template page from the
+ ``templates/classic/html/`` directory of the source to your tracker's
+ ``html`` directory.
+
+3. Once you've done that, edit the tracker's ``page.html`` template to
+ change::
+
+ <td rowspan="2" valign="top" class="sidebar">
+ <p class="classblock" tal:condition="request/user/queries">
+ <b>Your Queries</b><br>
+ <tal:block tal:repeat="qs request/user/queries">
+
+ to::
+
+ <td rowspan="2" valign="top" class="sidebar">
+ <p class="classblock">
+ <b>Your Queries</b> (<a href="query?@template=edit">edit</a>)<br>
+ <tal:block tal:repeat="qs request/user/queries">
+
+ That is, you're removing the ``tal:condition`` and adding a link to the
+ new edit page.
+
+4. You might also wish to remove the redundant query editing section from the
+ ``user.item.html`` page.
+
+
+0.7.0 Added Dispatcher role
+---------------------------
+
+A new config option has been added that specifies the email address of
+a "dispatcher" role. This email address acts as a central sentinel for
+issues coming into the system. You can configure it so that all e-mail
+error messages get bounced to them, them and the user in question, or
+just the user (default).
+
+To toggle these switches, add the "DISPATCHER_EMAIL" and
+"ERROR_MESSAGES_TO" configuration values to your tracker's ``config.py``.
+See the `customisation documentation`_ for how to use them.
-Migrating from 0.6 to 0.7
-=========================
0.7.0 Added CSV export action
-----------------------------
diff --git a/doc/user_guide.txt b/doc/user_guide.txt
index cf33487a7457abff6fb8632d3c2ee7eff558d8bb..19f44141e43237f5e723e8881c5e5ceb8c9d7fae 100644 (file)
--- a/doc/user_guide.txt
+++ b/doc/user_guide.txt
User Guide
==========
-:Version: $Revision: 1.25 $
+:Version: $Revision: 1.26 $
.. contents::
may type into the search form.
+Saving queries
+~~~~~~~~~~~~~~
+
+You may save queries in the tracker by giving the query a name. Each user
+may only have one query with a given name - if a subsequent search is
+performed with the same query name supplied, then it will edit the
+existing query of the same name.
+
+Queries may be marked as "private". These queries are only visible to the
+user that created them. If they're not marked "private" then all other
+users may include the query in their list of "Your Queries". Marking it as
+private at a later date does not affect users already using the query, nor
+does deleting the query.
+
+If a user subsequently creates or edits a public query, a new personal
+version of that query is made, with the same editing rules as described
+above.
+
Under the covers
~~~~~~~~~~~~~~~~
diff --git a/roundup/cgi/actions.py b/roundup/cgi/actions.py
index 529bfaca2d97ba2e9b57c2945bb16fd57edd8efa..481a971045f59c91f71de0060edbd92001c20635 100755 (executable)
--- a/roundup/cgi/actions.py
+++ b/roundup/cgi/actions.py
-#$Id: actions.py,v 1.16 2004-03-26 00:46:33 richard Exp $
+#$Id: actions.py,v 1.17 2004-03-26 04:50:50 richard Exp $
import re, cgi, StringIO, urllib, Cookie, time, random
# query string.
url = req.indexargs_href('', {})[1:]
- # handle editing an existing query
- try:
- qid = self.db.query.lookup(queryname)
- self.db.query.set(qid, klass=self.classname, url=url)
- except KeyError:
- # create a query
- qid = self.db.query.create(name=queryname,
- klass=self.classname, url=url)
+ key = self.db.query.getkey()
+ if key:
+ # edit the old way, only one query per name
+ try:
+ qid = self.db.query.lookup(queryname)
+ self.db.query.set(qid, klass=self.classname, url=url)
+ except KeyError:
+ # create a query
+ qid = self.db.query.create(name=queryname,
+ klass=self.classname, url=url)
+ else:
+ # edit the new way, query name not a key any more
+ # see if we match an existing private query
+ uid = self.db.getuid()
+ qids = self.db.query.filter({}, {'name': queryname,
+ 'private_for': uid})
+ if not qids:
+ # ok, so there's not a private query for the current user
+ # - see if there's a public one created by them
+ qids = self.db.query.filter({}, {'name': queryname,
+ 'private_for': -1, 'creator': uid})
+
+ if qids:
+ # edit query
+ qid = qids[0]
+ self.db.query.set(qid, klass=self.classname, url=url)
+ else:
+ # create a query
+ qid = self.db.query.create(name=queryname,
+ klass=self.classname, url=url, private_for=uid)
# and add it to the user's query multilink
queries = self.db.user.get(self.userid, 'queries')
return cl.create(**props)
class EditItemAction(_EditAction):
- def lastUserActivity(self):
+ def lastUserActvity(self):
if self.form.has_key(':lastactivity'):
- return date.Date(self.form[':lastactivity'].value)
+ user_actvity = date.Date(self.form[':lastactivity'].value)
elif self.form.has_key('@lastactivity'):
- return date.Date(self.form['@lastactivity'].value)
+ user_actvity = date.Date(self.form['@lastactivity'].value)
else:
return None
def lastNodeActivity(self):
cl = getattr(self.client.db, self.classname)
- return cl.get(self.nodeid, 'activity')
+ node_activity = cl.get(self.nodeid, 'activity')
- def detectCollision(self, userActivity, nodeActivity):
- # Result from lastUserActivity may be None. If it is, assume there's no
- # conflict, or at least not one we can detect.
- if userActivity:
- return userActivity < nodeActivity
+ def detectCollision(self, user_actvity, node_activity):
+ if user_activity:
+ return user_activity < node_activity
def handleCollision(self):
self.client.template = 'collision'
See parsePropsFromForm and _editnodes for special variables.
"""
- if self.detectCollision(self.lastUserActivity(), self.lastNodeActivity()):
+ user_activity = self.lastUserActvity()
+ if user_activity and self.detectCollision(user_activity,
+ self.lastNodeActivity()):
self.handleCollision()
return
url += '?@ok_message=%s&@template=%s'%(urllib.quote(message),
urllib.quote(self.template))
if self.nodeid is None:
- req = templating.HTMLRequest(self)
+ req = templating.HTMLRequest(self.client)
url += '&' + req.indexargs_href('', {})[1:]
raise Redirect, url
index ec223d9c68d27c0269fd0eee61085163f0ec2e2a..da9d29ed579e292334225f065566822de3556401 100644 (file)
raise
if self.templates.has_key(src) and \
- stime < self.templates[src].mtime:
+ stime <= self.templates[src].mtime:
# compiled template is up to date
return self.templates[src]
content_type = mimetypes.guess_type(filename)[0] or 'text/html'
pt.pt_edit(open(src).read(), content_type)
pt.id = filename
- pt.mtime = time.time()
+ pt.mtime = stime
return pt
def __getitem__(self, name):
'tracker': client.instance,
'utils': utils(client),
'templates': Templates(client.instance.config.TEMPLATES),
+ 'template': self,
}
# add in the item if there is one
if client.nodeid:
def designator(self):
"""Return this item's designator (classname + id)."""
return '%s%s'%(self._classname, self._nodeid)
+
+ def is_retired(self):
+ """Is this item retired?"""
+ return self._klass.is_retired(self._nodeid)
def submit(self, label="Submit Changes"):
"""Generate a submit button.
''' Support the "in" operator. We have to make sure the passed-in
value is a string first, not a HTMLProperty.
'''
+ print (self, value, self._value)
return str(value) in self._value
def reverse(self):
index 2e8c4741194b725576d5ed678038480b2e487828..f38dcabcf010f95e1a2da2393c27f508f54a6209 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: dbinit.py,v 1.6 2004-03-15 05:51:23 richard Exp $
+# $Id: dbinit.py,v 1.7 2004-03-26 04:50:50 richard Exp $
import os
query = Class(db, "query",
klass=String(), name=String(),
- url=String())
- query.setkey("name")
+ url=String(), private_for=Link('user'))
# add any additional database schema configuration here
diff --git a/templates/classic/html/_generic.collision.html b/templates/classic/html/_generic.collision.html
index fd2cc2b23d6a710cc22ad8b61dc7c5f3d2664061..34dbece1b2692e0d2e4b7a6ae7d6b48caf3289b0 100644 (file)
editing. Please <a tal:attributes="href context/designator">reload</a>
the node and review your edits.
</td>
-</tal:block>
\ No newline at end of file
+</tal:block>
+
index 916d35f96804e7224144b33b503e4e0c485a131b..cf35212cff1e59512795c645844d266bd9dcb23f 100644 (file)
<tr>
<td rowspan="2" valign="top" class="sidebar">
- <p class="classblock" tal:condition="request/user/queries">
- <b>Your Queries</b><br>
+ <p class="classblock">
+ <b>Your Queries</b> (<a href="query?@template=edit">edit</a>)<br>
<tal:block tal:repeat="qs request/user/queries">
<a tal:attributes="href string:${qs/klass}?${qs/url}"
tal:content="qs/name">link</a><br>
tal:attributes="value name;
checked python:name == group_on">
</td>
+<!-- SHA: 9defd15b86478f539e44f06b9548340e239d7320 -->
diff --git a/templates/classic/html/query.edit.html b/templates/classic/html/query.edit.html
--- /dev/null
@@ -0,0 +1,105 @@
+<!-- dollarId: user.item,v 1.7 2002/08/16 04:29:04 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title">
+<span tal:replace="config/TRACKER_NAME" />: "Your Queries" Editing
+</title>
+<span metal:fill-slot="body_title" tal:omit-tag="python:1">
+ "Your Queries" Editing
+</span>
+
+<td class="content" metal:fill-slot="content">
+
+<span tal:condition="not:context/is_edit_ok">
+You are not allowed to edit queries.
+</span>
+
+<script language="javascript">
+// This exists solely because I can't figure how to get the & into an
+// attributes TALES expression, and so it keeps getting quoted.
+function retire(qid) {
+ window.location = 'query'+qid+'?@action=retire&@template=edit';
+}
+</script>
+
+<form method="POST" onSubmit="return submit_once()" action="query"
+ enctype="multipart/form-data" tal:condition="context/is_edit_ok">
+
+<table class="list" width="100%"
+ tal:define="uid request/user/id; mine request/user/queries">
+
+<tr><th>Query</th>
+ <th>Include in "Your Queries"</th>
+ <th>Edit</th>
+ <th>Private to you?</th>
+ <th> </th>
+</tr>
+
+<tr tal:repeat="query mine">
+ <tal:block condition="query/is_retired">
+
+ <td><a tal:attributes="href string:${query/klass}?${query/url}"
+ tal:content="query/name">query</a></td>
+
+ <td metal:define-macro="include">
+ <select tal:condition="python:query.id not in mine"
+ tal:attributes="name string:user${uid}@add@queries">
+ <option value="">leave out</option>
+ <option tal:attributes="value query/id">include</option>
+ </select>
+ <select tal:condition="python:query.id in mine"
+ tal:attributes="name string:user${uid}@remove@queries">
+ <option value="">leave in</option>
+ <option tal:attributes="value query/id">remove</option>
+ </select>
+ </td>
+
+ <td colspan="3">[query is retired]</td>
+
+ <!-- <td> maybe offer "restore" some day </td> -->
+ </tal:block>
+</tr>
+
+<tr tal:define="queries python:db.query.filter(filterspec={'private_for':uid})"
+ tal:repeat="query queries">
+ <td><a tal:attributes="href string:${query/klass}?${query/url}"
+ tal:content="query/name">query</a></td>
+
+ <td metal:use-macro="template/macros/include" />
+
+ <td><a tal:attributes="href string:query${query/id}">edit</a></td>
+
+ <td>
+ <select tal:attributes="name string:query${query/id}@private_for">
+ <option tal:attributes="selected python:query.private_for == uid;
+ value uid">yes</option>
+ <option tal:attributes="selected python:query.private_for == None"
+ value="-1">no</option>
+ </select>
+ </td>
+
+ <td>
+ <input type="button" value="Delete"
+ tal:attributes="onClick python:'''retire('%s')'''%query.id">
+ </td>
+</tr>
+
+<tr tal:define="queries python:db.query.filter(filterspec={'private_for':None})"
+ tal:repeat="query queries">
+ <td><a tal:attributes="href string:${query/klass}?${query/url}"
+ tal:content="query/name">query</a></td>
+
+ <td metal:use-macro="template/macros/include" />
+ <td colspan="3">[not yours to edit]</td>
+</tr>
+
+<tr><td colspan="5">
+ <input type="hidden" name="@action" value="edit">
+ <input type="hidden" name="@template" value="edit">
+ <input type="submit" value="Save Selection">
+</td></tr>
+
+</table>
+
+</form>
+</td>
+</tal:block>
index 2410b2cb4b9ffc2b5787ac405964ffb865b2311c..dae4b7356095b9e0c60a7ade2bbd77ba3322259d 100644 (file)
</table>
</form>
-<table class="otherinfo" tal:condition="python:context.queries and context.is_view_ok()">
- <tr><th colspan="3" class="header">Queries</th></tr>
- <tr><th>Name</th><th colspan="2">Actions</th></tr>
- <tr tal:repeat="query context/queries">
- <td><a tal:attributes="href string:query${query/id}"
- tal:content="query/name"></a></td>
- <td>
- <a tal:attributes="href string:${query/klass}?${query/url}">display</a>
- </td>
- <td>
- <a tal:attributes="href string:?@remove@queries=${query/id}&@action=edit">remove</a>
- </td>
- </tr>
-</table>
-
<table class="form" tal:condition="context/is_only_view_ok">
<tr>
<th colspan=2 class="header" tal:content="context/realname">realname</th>