Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

well, missing some file from prev commit :S

  • Loading branch information...
commit e1eb711070169c731044c376650ed8a318cee982 1 parent e7d1817
@simahawk simahawk authored
Showing with 39 additions and 5,374 deletions.
  1. +26 −1 README.rst
  2. +2 −2 buildout.cfg
  3. +2 −0  docs/HISTORY.txt
  4. +9 −5 setup.py
  5. +0 −6 src/abstract/__init__.py
  6. +0 −4 src/abstract/simplemanagement/__init__.py
  7. +0 −62 src/abstract/simplemanagement/booking.py
  8. +0 −36 src/abstract/simplemanagement/booking_templates/view.pt
  9. 0  src/abstract/simplemanagement/browser/__init__.py
  10. +0 −4 src/abstract/simplemanagement/browser/comments.py
  11. +0 −141 src/abstract/simplemanagement/browser/configure.zcml
  12. +0 −123 src/abstract/simplemanagement/browser/dashboard.py
  13. +0 −9 src/abstract/simplemanagement/browser/macros.py
  14. +0 −19 src/abstract/simplemanagement/browser/navigation.py
  15. +0 −34 src/abstract/simplemanagement/browser/overrides.py
  16. +0 −87 src/abstract/simplemanagement/browser/quickforms.py
  17. BIN  src/abstract/simplemanagement/browser/static/magnifier.png
  18. BIN  src/abstract/simplemanagement/browser/static/pencil.png
  19. +0 −444 src/abstract/simplemanagement/browser/static/simplemanagement.css
  20. +0 −251 src/abstract/simplemanagement/browser/static/simplemanagement.js
  21. BIN  src/abstract/simplemanagement/browser/static/spindown-closed.gif
  22. BIN  src/abstract/simplemanagement/browser/static/spindown-open.gif
  23. BIN  src/abstract/simplemanagement/browser/static/tag_red.png
  24. +0 −73 src/abstract/simplemanagement/browser/story_move.py
  25. +0 −142 src/abstract/simplemanagement/browser/templates/dashboard.pt
  26. +0 −361 src/abstract/simplemanagement/browser/templates/macros.pt
  27. +0 −24 src/abstract/simplemanagement/browser/templates/mystories.pt
  28. +0 −23 src/abstract/simplemanagement/browser/templates/mytickets.pt
  29. +0 −26 src/abstract/simplemanagement/browser/templates/wf_actions.pt
  30. +0 −50 src/abstract/simplemanagement/browser/wfactions.py
  31. +0 −34 src/abstract/simplemanagement/configure.py
  32. +0 −62 src/abstract/simplemanagement/configure.zcml
  33. +0 −111 src/abstract/simplemanagement/epic.py
  34. +0 −112 src/abstract/simplemanagement/epic_templates/view.pt
  35. +0 −67 src/abstract/simplemanagement/events.py
  36. +0 −20 src/abstract/simplemanagement/indexers.py
  37. +0 −190 src/abstract/simplemanagement/interfaces.py
  38. +0 −91 src/abstract/simplemanagement/iteration.py
  39. +0 −1  src/abstract/simplemanagement/iteration_templates/story_form.pt
  40. +0 −44 src/abstract/simplemanagement/iteration_templates/view.pt
  41. +0 −534 src/abstract/simplemanagement/locales/abstract.simplemanagement.pot
  42. +0 −534 src/abstract/simplemanagement/locales/it/LC_MESSAGES/abstract.simplemanagement.po
  43. +0 −30 src/abstract/simplemanagement/locales/rebuild.sh
  44. +0 −5 src/abstract/simplemanagement/profiles/default/browserlayer.xml
  45. +0 −19 src/abstract/simplemanagement/profiles/default/catalog.xml
  46. +0 −15 src/abstract/simplemanagement/profiles/default/cssregistry.xml
  47. +0 −11 src/abstract/simplemanagement/profiles/default/jsregistry.xml
  48. +0 −9 src/abstract/simplemanagement/profiles/default/metadata.xml
  49. +0 −8 src/abstract/simplemanagement/profiles/default/propertiestool.xml
  50. +0 −10 src/abstract/simplemanagement/profiles/default/types.xml
  51. +0 −44 src/abstract/simplemanagement/profiles/default/types/Booking.xml
  52. +0 −50 src/abstract/simplemanagement/profiles/default/types/Epic.xml
  53. +0 −51 src/abstract/simplemanagement/profiles/default/types/Iteration.xml
  54. +0 −60 src/abstract/simplemanagement/profiles/default/types/Project.xml
  55. +0 −50 src/abstract/simplemanagement/profiles/default/types/Story.xml
  56. +0 −22 src/abstract/simplemanagement/profiles/default/workflows.xml
  57. +0 −143 src/abstract/simplemanagement/profiles/default/workflows/project_workflow/definition.xml
  58. +0 −108 src/abstract/simplemanagement/profiles/default/workflows/story_workflow/definition.xml
  59. +0 −200 src/abstract/simplemanagement/project.py
  60. +0 −62 src/abstract/simplemanagement/project_templates/backlog.pt
  61. +0 −174 src/abstract/simplemanagement/project_templates/overview.pt
  62. +0 −71 src/abstract/simplemanagement/project_templates/planning.pt
  63. +0 −12 src/abstract/simplemanagement/project_templates/stories.pt
  64. +0 −85 src/abstract/simplemanagement/project_templates/view.pt
  65. +0 −85 src/abstract/simplemanagement/stories_listing.py
  66. +0 −87 src/abstract/simplemanagement/story.py
  67. +0 −1  src/abstract/simplemanagement/story_templates/booking_form.pt
  68. +0 −44 src/abstract/simplemanagement/story_templates/view.pt
  69. +0 −58 src/abstract/simplemanagement/structures.py
  70. +0 −100 src/abstract/simplemanagement/utils.py
  71. +0 −33 src/abstract/simplemanagement/vocabularies.py
View
27 README.rst
@@ -1,4 +1,29 @@
Simple Management
=================
-Fill this in!
+SimpleManagement, an agile project management tool built on top of `Plone`__ that aims at merging `eXtreme Management` and `SCRUM` approaches. It's based on `Dexterity`__.
+
+Some features
+-------------
+
+ - automatic creation of documentation folder
+ - POI tracker and project form
+ - iterations where you can load/move/work on stories in agile mood ala ScrumDo
+ - planning view to move/clone stories between different iterations in agile mood
+ - user dashboard where stories and ticket relevant to the user show up
+ - booking on the stories (no need to create tasks as mandatory in XM, too often a level/layer too far for our needs)
+
+For a wider view on what this is about take a look at `Maurizio's blogpost`__ and `watch the video`__.
+
+
+Installation
+------------
+
+Please, refer to `official documentation`__.
+
+
+__ http://pypi.python.org/pypi/Plone
+__ http://pypi.python.org/pypi/plone.dexterity
+__ http://www.abstract.it/en/blog/maurizio-delmonte/simplemanagement-an-agile-project-management-tool
+__ http://vimeo.com/51785910
+__ http://plone.org/documentation/kb/add-ons/tutorial-all-pages
View
4 buildout.cfg
@@ -2,7 +2,7 @@
extends =
http://svn.plone.org/svn/collective/buildout/plonetest/plone-4.2.x.cfg
-package-name = abstract.simplemanagement
+package-name = collective.simplemanagement
parts +=
test
@@ -29,7 +29,7 @@ eggs +=
recipe = zc.recipe.testrunner
eggs =
${instance:eggs}
- abstract.simplemanagement [test]
+ collective.simplemanagement [test]
defaults = ['-s', '${buildout:package-name}']
View
2  docs/HISTORY.txt
@@ -4,6 +4,8 @@ Changelog
1.0 (unreleased)
----------------
+- moved to collective namespace [simahawk]
+- prevent project view to brake if project created by zope admin [simahawk]
- users' dashboard enhancements [gborelli]
- added server side method to sort stories in iteration view [gborelli]
- Change story status from iteration view fixes #9 [gborelli]
View
14 setup.py
@@ -4,13 +4,17 @@
VERSION = '1.0'
+LONG_DESC = '\n'.join([
+ open("README.rst").read(),
+ open(os.path.join("docs", "CONTRIBUTORS.rst")).read(),
+ open(os.path.join("docs", "HISTORY.txt")).read(),
+])
setup(
- name='abstract.simplemanagement',
+ name='collective.simplemanagement',
version=VERSION,
description="The project management platform",
- long_description=open("README.rst").read() + "\n" +
- open(os.path.join("docs", "HISTORY.txt")).read(),
+ long_description=LONG_DESC,
classifiers=[
"Framework :: Plone",
"Programming Language :: Python",
@@ -18,11 +22,11 @@
keywords='plone project management',
author='Simone Deponti',
author_email='simone.deponti@abstract.it',
- url='https://github.com/abstract-open-solutions/abstract.simplemanagement',
+ url='https://github.com/collective/collective.simplemanagement',
license='BSD',
packages=find_packages('src'),
package_dir={'': 'src'},
- namespace_packages=['abstract'],
+ namespace_packages=['collective'],
include_package_data=True,
zip_safe=False,
install_requires=[
View
6 src/abstract/__init__.py
@@ -1,6 +0,0 @@
-# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
-try:
- __import__('pkg_resources').declare_namespace(__name__)
-except ImportError: #pragma: no cover
- from pkgutil import extend_path
- __path__ = extend_path(__path__, __name__)
View
4 src/abstract/simplemanagement/__init__.py
@@ -1,4 +0,0 @@
-from zope.i18nmessageid import MessageFactory
-
-messageFactory = MessageFactory('abstract.simplemanagement')
-
View
62 src/abstract/simplemanagement/booking.py
@@ -1,62 +0,0 @@
-from datetime import date
-
-from five import grok
-from z3c.form import form, field
-from z3c.relationfield.relation import create_relation
-
-from plone.directives import dexterity
-from plone.dexterity.utils import createContentInContainer
-
-from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-from .interfaces import IBooking
-from .interfaces import IQuickForm
-
-
-class BookingForm(form.AddForm):
- template = ViewPageTemplateFile("story_templates/booking_form.pt")
- fields = field.Fields(IQuickForm).select('title') + \
- field.Fields(IBooking).select('time')
-
- name = 'booking_form'
-
- convert_funcs = {
- 'related': lambda x: create_relation('/'.join(x.getPhysicalPath()))
- }
-
- def create(self, data):
- item = createContentInContainer(
- self.context,
- 'Booking',
- title=data.pop('title'))
- for k, v in data.items():
- if v and k in self.convert_funcs:
- v = self.convert_funcs[k](v)
- setattr(item, k, v)
- item.date = date.today()
- return item
-
- def add(self, obj):
- obj.reindexObject()
-
- def nextURL(self):
- return self.context.absolute_url()
-
-
-class Booking(dexterity.Item):
- grok.implements(IBooking)
-
- def get_related(self):
- related = self.related
- if bool(related) and \
- not related.isBroken():
- related = related.to_object
- return {
- 'title': related.Title(),
- 'href': related.absolute_url(),
- 'description': related.Description(),
- }
-
-
-class View(grok.View):
- grok.context(IBooking)
- grok.require('zope2.View')
View
36 src/abstract/simplemanagement/booking_templates/view.pt
@@ -1,36 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal"
- xmlns:metal="http://xml.zope.org/namespaces/metal"
- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
- lang="en"
- metal:use-macro="context/main_template/macros/master"
- i18n:domain="abstract.simplemanagement">
-<body>
-
-<metal:content-core fill-slot="content-core">
- <metal:block define-macro="content-core">
-
- <dl>
- <dt i18n:translate="">Date</dt>
- <dd tal:content="python:context.toLocalizedTime(context.date.isoformat())"></dd>
- <dt i18n:translate="">Time</dt>
- <dd tal:content="context/time"></dd>
- <tal:block
- define="related view/context/get_related"
- condition="related">
- <dt i18n:translate="">Related</dt>
- <dd>
- <a href=""
- title=""
- tal:content="related/title"
- tal:attributes="href related/href;
- title related/description"></a>
- </dd>
- </tal:block>
- </dl>
-
- </metal:block>
-</metal:content-core>
-
-</body>
-</html>
View
0  src/abstract/simplemanagement/browser/__init__.py
No changes.
View
4 src/abstract/simplemanagement/browser/comments.py
@@ -1,4 +0,0 @@
-class ConversationView(object):
- # enable comment for Story content type
- def enabled(self):
- return True
View
141 src/abstract/simplemanagement/browser/configure.zcml
@@ -1,141 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:i18n="http://namespaces.zope.org/i18n"
- xmlns:gs="http://namespaces.zope.org/genericsetup"
- xmlns:browser="http://namespaces.zope.org/browser"
- i18n_domain="abstract.simplemanagement">
-
- <browser:resourceDirectory
- directory="static"
- name="simplemanagement"
- layer="..interfaces.IBrowserLayer"
- />
-
- <browser:page
- for="..interfaces.IStory"
- name="quickedit"
- class=".quickforms.StoryQuickedit"
- layer="..interfaces.IBrowserLayer"
- permission="zope2.View"
- />
-
- <browser:pages
- for="..interfaces.IStory"
- class=".wfactions.WFActions"
- layer="..interfaces.IBrowserLayer"
- permission="zope2.View">
- <browser:page
- attribute="wf_actions"
- name="wf_actions"
- />
- <browser:page
- attribute="change_status"
- name="change_status"
- />
- </browser:pages>
-
- <browser:page
- for="..interfaces.IIteration"
- name="story_move"
- class=".story_move.StoryMove"
- layer="..interfaces.IBrowserLayer"
- permission="cmf.ModifyPortalContent"
- />
-
- <browser:page
- for="..interfaces.IProject"
- name="story_move"
- class=".story_move.StoryMove"
- layer="..interfaces.IBrowserLayer"
- permission="cmf.ModifyPortalContent"
- />
-
- <browser:page
- for="*"
- name="simpemanagement-macros"
- class=".macros.Macros"
- layer="..interfaces.IBrowserLayer"
- permission="zope2.View"
- />
-
- <!-- allow comments for story items -->
- <browser:page
- name="conversation_view"
- for="..interfaces.IStory"
- layer="..interfaces.IBrowserLayer"
- class=".comments.ConversationView"
- permission="zope2.View"
- />
-
- <!-- dashboard -->
- <browser:page
- for="plone.app.layout.navigation.interfaces.INavigationRoot"
- name="dashboard"
- layer="..interfaces.IBrowserLayer"
- permission="plone.app.portlets.ViewDashboard"
- class=".dashboard.DashboardView"
- template="templates/dashboard.pt"
- />
-
- <browser:page
- for="plone.app.layout.navigation.interfaces.INavigationRoot"
- name="mytickets"
- class=".dashboard.MyTickets"
- template="templates/mytickets.pt"
- layer="..interfaces.IBrowserLayer"
- permission="zope2.View"
- />
-
- <browser:page
- for="plone.app.layout.navigation.interfaces.INavigationRoot"
- name="mystories"
- class=".dashboard.MyStories"
- template="templates/mystories.pt"
- layer="..interfaces.IBrowserLayer"
- permission="zope2.View"
- />
-
- <!-- TODO: fix adapter for attribute... -->
- <adapter
- for="*"
- provides="..interfaces.IQuickForm"
- factory=".quickforms.BaseqQuickFormAdapter"
- />
-
- <!-- Custom navtree strategy for Project -->
- <class class=".navigation.ProjectNavtreeStrategy">
- <allow
- interface="plone.app.layout.navigation.interfaces.INavtreeStrategy" />
- </class>
- <adapter factory=".navigation.ProjectNavtreeStrategy"
- for="..interfaces.IProject
- plone.app.portlets.portlets.navigation.INavigationPortlet" />
- <adapter factory=".navigation.ProjectNavtreeStrategy"
- for="..interfaces.IIteration
- plone.app.portlets.portlets.navigation.INavigationPortlet" />
- <adapter factory=".navigation.ProjectNavtreeStrategy"
- for="..interfaces.IStory
- plone.app.portlets.portlets.navigation.INavigationPortlet" />
- <adapter factory=".navigation.ProjectNavtreeStrategy"
- for="..interfaces.IEpic
- plone.app.portlets.portlets.navigation.INavigationPortlet" />
-
- <!-- overrides -->
- <browser:viewlet
- name="plone.path_bar"
- manager="plone.app.layout.viewlets.interfaces.IAboveContent"
- class=".overrides.BacklogPathBarViewlet"
- permission="zope2.View"
- view="..interfaces.IBacklogView"
- />
-
- <browser:viewlet
- name="plone.path_bar"
- manager="plone.app.layout.viewlets.interfaces.IAboveContent"
- class=".overrides.BacklogStoryPathBarViewlet"
- for="..interfaces.IStory"
- permission="zope2.View"
- />
-
-
-</configure>
View
123 src/abstract/simplemanagement/browser/dashboard.py
@@ -1,123 +0,0 @@
-from zope.component import getMultiAdapter
-
-from plone.app.layout.dashboard import dashboard
-from plone.memoize.instance import memoize
-
-from Products.CMFCore.utils import getToolByName
-from Products.Five.browser import BrowserView
-
-
-class DashboardMixin(object):
- query_extra_params = {
- 'sort_on': 'modified',
- 'sort_order': 'descending'
- }
-
- @memoize
- def tools(self):
- return {
- 'portal_catalog': getToolByName(self.context, 'portal_catalog')
- }
-
- @memoize
- def portal_state(self):
- return getMultiAdapter(
- (self.context, self.request),
- name='plone_portal_state'
- )
-
- @property
- def user(self):
- user = None
- portal_state = self.portal_state()
- if not portal_state.anonymous():
- user = portal_state.member()
- return user
-
- def get_story(self, brain):
- story = brain.getObject()
- iteration = story.getParentNode()
- project = iteration.getParentNode()
- return {
- 'title': brain.Title,
- 'description': brain.Description,
- 'url': brain.getURL(),
- 'status': brain.review_state,
- 'can_edit': story.user_can_edit(),
- 'can_review': story.user_can_review(),
- 'iteration': {
- 'title': iteration.Title(),
- 'description': iteration.Description(),
- 'url': iteration.absolute_url()
- },
- 'project': {
- 'title': project.Title(),
- 'description': project.Description(),
- 'url': project.absolute_url()
- }
- }
-
- @property
- def searches(self):
- if self.portal_state().anonymous():
- return []
-
- return [
- ({'portal_type': 'PoiIssue',
- 'getResponsibleManager': self.user.getId(),
- 'review_state': ('new', 'open', 'in-progress', 'resolved',
- 'unconfirmed')},
- 'tickets'),
- ({'portal_type': 'Story',
- 'assigned_to': self.user.getId(),
- 'review_state': ('todo', 'suspended', 'in_progress')},
- 'stories')
- ]
-
-
-class MyTickets(BrowserView, DashboardMixin):
-
- def tickets(self):
- query = self.searches[0][0]
- query.update(self.query_extra_params)
- pc = self.tools()['portal_catalog']
- return pc.searchResults(query)
-
-
-class MyStories(BrowserView, DashboardMixin):
-
- def stories(self):
- query = self.searches[1][0]
- query.update(self.query_extra_params)
- pc = self.tools()['portal_catalog']
- return [self.get_story(i) for i in pc.searchResults(query)]
-
-
-class DashboardView(dashboard.DashboardView, DashboardMixin):
-
- MAX_ELEMENTS = 5
-
- def format_results(self, brain, res_type):
- if res_type == 'tickets':
- return brain
- else:
- return self.get_story(brain)
-
- def dashboard(self):
- result = {
- 'tickets': [],
- 'tickets_n': 0,
- 'stories': [],
- 'stories_n': 0
- }
-
- pc = self.tools()['portal_catalog']
- for query, result_key in self.searches:
- query.update(self.query_extra_params)
- results = pc.searchResults(query)
- result[result_key] = [self.format_results(i, result_key) \
- for i in results[:self.MAX_ELEMENTS]]
- result['%s_n' % result_key] = total = len(results)
- if total <= self.MAX_ELEMENTS:
- result['%s_n' % result_key] = False
- return result
View
9 src/abstract/simplemanagement/browser/macros.py
@@ -1,9 +0,0 @@
-from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-from zope.publisher.browser import BrowserView
-
-
-class Macros(BrowserView):
- template = ViewPageTemplateFile('templates/macros.pt')
-
- def __getitem__(self, key):
- return self.template.macros[key]
View
19 src/abstract/simplemanagement/browser/navigation.py
@@ -1,19 +0,0 @@
-from zope.interface import implements
-from plone.app.layout.navigation.interfaces import INavtreeStrategy
-from plone.app.portlets.portlets.navigation import NavtreeStrategy
-
-
-class ProjectNavtreeStrategy(NavtreeStrategy):
-
- implements(INavtreeStrategy)
-
- hidden_types = ('Story', 'Epic')
-
- def nodeFilter(self, node):
- result = super(ProjectNavtreeStrategy, self).nodeFilter(node)
- item = node['item']
- if result:
- if item.portal_type in self.hidden_types:
- result = False
- return result
-
View
34 src/abstract/simplemanagement/browser/overrides.py
@@ -1,34 +0,0 @@
-from plone.app.layout.viewlets.common import PathBarViewlet
-from ..interfaces import IProject
-from .. import messageFactory as _
-
-
-class BacklogPathBarViewlet(PathBarViewlet):
-
- def update(self):
- super(BacklogPathBarViewlet, self).update()
- project = self.breadcrumbs[-1] # pylint: disable=E0203
- self.breadcrumbs = self.breadcrumbs + (
- { 'absolute_url': project['absolute_url'].rstrip('/') + \
- '/@@backlog',
- 'Title': _(u"Backlog") },
- )
-
-
-class BacklogStoryPathBarViewlet(PathBarViewlet):
-
- def update(self):
- super(BacklogStoryPathBarViewlet, self).update()
- if IProject.providedBy(self.context.__parent__):
- # pylint: disable=E0203
- project = self.breadcrumbs[-2]
- story = self.breadcrumbs[-1]
- backlog = {
- 'absolute_url': project['absolute_url'].rstrip('/') + \
- '/@@backlog',
- 'Title': _(u"Backlog")
- }
- self.breadcrumbs = self.breadcrumbs[:-1] + (
- backlog,
- story
- )
View
87 src/abstract/simplemanagement/browser/quickforms.py
@@ -1,87 +0,0 @@
-# pylint: disable=W0613
-from zope.interface import implements
-
-from z3c.form import form, field, button
-from plone.z3cform.layout import wrap_form
-
-from Products.CMFCore.utils import getToolByName
-# from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-
-from abstract.z3cform.usertokeninput.widget import UserTokenInputFieldWidget
-
-from ..interfaces import IStory
-from ..interfaces import IQuickForm
-
-from .. import messageFactory as _
-
-
-class BaseqQuickFormAdapter(object):
- implements(IQuickForm)
-
- def __init__(self, context):
- self.context = context
-
- def get_title(self):
- return self.context.Title()
-
- def set_title(self, value):
- self.context.title = value
-
- title = property(get_title, set_title)
-
- def get_description(self):
- return self.context.Description()
-
- def set_description(self, value):
- self.context.description = value
-
- description = property(get_description, set_description)
-
-
-class BaseQuickeEdit(form.EditForm):
- name = "quickedit_form"
- noChangesMessage = _(u"No changes were applied.")
- successMessage = _(u'Data successfully updated.')
-
- def redirect(self):
- self.request.response.redirect(
- location='%s/quickedit?ajax_load=1&ajax_include_head=1' % \
- self.context.absolute_url()
- )
-
- @button.buttonAndHandler(_(u'Save'), name='save')
- def handleApply(self, action):
- data, errors = self.extractData()
- if errors:
- self.status = self.formErrorsMessage
- return
- changes = self.applyChanges(data)
- if changes:
- self.status = self.successMessage
- else:
- self.status = self.noChangesMessage
-
- ptool = getToolByName(self.context, 'plone_utils')
- ptool.addPortalMessage(self.status)
- self.redirect()
-
- @button.buttonAndHandler(_(u'Cancel'), name='cancel')
- def handleCancel(self, action):
- ptool = getToolByName(self.context, 'plone_utils')
- ptool.addPortalMessage(self.noChangesMessage)
- self.redirect()
-
-
-class StoryQuickeditForm(BaseQuickeEdit):
- fields = field.Fields(IQuickForm) + field.Fields(IStory).select(
- 'text',
- 'estimate',
- 'assigned_to',
- 'epic')
- fields['assigned_to'].widgetFactory = UserTokenInputFieldWidget
-
-
-StoryQuickedit = wrap_form(
- StoryQuickeditForm,
- # index=ViewPageTemplateFile('templates/quickedit_form.pt')
-)
View
BIN  src/abstract/simplemanagement/browser/static/magnifier.png
Deleted file not rendered
View
BIN  src/abstract/simplemanagement/browser/static/pencil.png
Deleted file not rendered
View
444 src/abstract/simplemanagement/browser/static/simplemanagement.css
@@ -1,444 +0,0 @@
-#project-view {margin-top: 1em}
-
-#project-iterations h2 {
- padding: .1em 0 .1em 1em;
- font-size: 120%;
- border-bottom: 1px solid #ccc;
- border-top:1px solid white;
- background: #eeeeee; /* Old browsers */
-}
-
-.pane {
- padding: 1em;
-}
-
-#overview .panes {
- clear: both;
- height: 220px;
- overflow: hidden;
-}
-
-#overview .panes > div {
- border-color: #C8CACA;
- border-style: solid;
- border-width: 0 1px 1px;
- display: none;
- height: 180px;
- padding: 15px 20px;
- overflow-y: auto;
-}
-
-
-#overview ul.tabs li {
- float: left;
- list-style-type: none;
- margin: 0;
- padding: 0;
-}
-
-#overview ul.tabs a:hover {
- color: black !important;
-}
-
-#overview ul.tabs a {
- background-color: #EEEEEE;
- border: 1px solid #C8CACA;
- border-width: 1px 1px 0;
- color: #777777;
- display: block;
- float: left;
- font-size: 13px;
- height: 18px;
- margin-right: 2px;
- outline: 0 none;
- padding: 5px 30px;
- position: relative;
- text-decoration: none;
- top: 1px;
-}
-
-#overview ul.tabs a.current {
- border-bottom: 1px solid white;
- background-color: white;
- color: #000000;
- cursor: default;
-}
-
-#overview ul.tabs {
- border-bottom: 1px solid #C8CACA;
- height: 30px;
- margin: 0 !important;
- padding: 0;
-}
-
-
-#overview {
- margin: 1em 0;
-}
-
-.project-details {
- float: left;
- width: 49%;
-}
-
-#iteration-dates {
- font-size: 125%;
-}
-
-#content-core table.listing {
- margin-bottom: 1em;
-}
-
-/* Booking form */
-.booking {
- margin-top: 1em;
-}
-
-.booking .listing{
- font-size: 85%;
- width: 100%;
-}
-
-.booking .listing h4 {
- font-size: 110%;
-}
-
-.booking .listing .discreet {
- font-size: 100%
-}
-
-#booking-form {
- margin-top: 1em;
-}
-
-#booking-form label,
-#booking-form .field {
- float: left;
- clear: none;
- margin-right: 1em;
-}
-
-#booking-form .fieldErrorBox {
- display: inline;
-}
-
-
-#booking-form .field input[type="text"][name$=".title"],
-#content #booking-form input.text-widget {
- line-height: 100%;
- font-size: 100%;
- width: 30em;
-}
-
-#content #booking-form input#form-widgets-time {
- width: 30px;
-}
-
-#content ul.stories {
- margin: 1.3em 0 0;
- list-style-type: none;
-}
-
-.story-container {
- border-bottom: 1px solid #E1E1E1;
- border-right: 1px solid #E1E1E1;
- border-top: 1px solid #E1E1E1;
- list-style-type: none;
- margin: 0 0 5px;
- min-height: 33px;
- padding: 3px 3px 3px 10px;
- position: relative;
- display: list-item;
-}
-
-.sortable .story-container {
- cursor: move;
-}
-
-.story-container > div {
- cursor: default;
- background-color: white;
-}
-
-.todo {
- border-left: 6px solid #444444;
-}
-
-.done {
- border-left: 6px solid #8DC73E;
-}
-
-.in_progress {
- border-left: 6px solid #448CCA;
-}
-
-.suspended {
- border-left: 6px solid #FF7F0E;
-}
-
-.ui-sortable-placeholder {
- border: 1px dotted black !important;
- visibility: visible !important;
- height: 20px !important;
- background-color: #FEF9DD;
-}
-
-.ui-sortable-placeholder * {
- visibility: hidden;
-}
-
-
-.portaltype-story .timing {
- border: 1px solid #C8CACA;
-}
-.story-block .timing {
- width: 20em;
-}
-.timing {
- border: 1px solid #C8CACA;
- float: right;
- margin-right: -4px;
- margin-top: -4px;
- margin-left: 1em;
- vertical-align: middle;
-}
-
-.timing th {
- color: #666666
-}
-.timing td,
-.timing th {
- border-left: 1px solid #C8CACA;
- padding: .2em;
- background-color: #DDDDDD;
-}
-
-.timing td {
- border-top: 1px solid #C8CACA;
- text-align: right;
- background-color: white;
-}
-
-/* timing status */
-td.danger {
- background-color: red;
- color: white;
-}
-
-td.success {
- background-color: #75AD0A;
- color: white;
-}
-
-td.warning {
- background-color: yellow;
-}
-
-td.estimate {
- width: 55px;
-}
-td.actual_hours {
- width: 85px;
- text-align: right;
-}
-td.difference {
- width: 65px;
-}
-
-.totals {
- margin-top: 1em;
-}
-.totals .timing {
- margin-right: 0px;
- margin-top: 0px;
-}
-
-.totals-label {
- vertical-align: bottom
-}
-
-.assignees-container .assignees {
- display: inline
-}
-
-.assignees-container .assignees li {
- display:inline !important;
- margin-right: .5em;
-}
-
-
-#content ul.assignees {
- list-style-type: none;
- margin-left: 0;
-}
-
-.extra-details {
- margin-top: 1em;
-}
-
-.clearfix:after {
- content: ".";
- display: block;
- height: 0;
- clear: both;
- visibility: hidden;
-}
-
-
-.actions {
- float: right;
- margin: .5em 0 0;
-}
-
-.story-container.copy {
- cursor: copy;
-}
-
-#content a.status,
-#content a.quickedit,
-#content a.story-quickview {
- background: transparent url('pencil.png') no-repeat;
- border: 0 none !important;
- display: block;
- float: left;
- height: 16px;
- margin-left: 0.3em;
- overflow: hidden;
- width: 16px;
-}
-
-.template-quickedit #portal-column-content {
- margin-left: -98.875%;
- width: 96.75%;
- padding: .5em;
-}
-
-#content a.story-quickview {
- background-image: url('magnifier.png');
-}
-
-/*#dashboard a.story-quickview {
- margin: 5px 5px 0 0;
-}*/
-
-#content a.status {
- background-image: url('tag_red.png');
-}
-
-#content a.status span,
-#content a.story-quickview span,
-#content a.quickedit span {
- padding: 20px;
-}
-
-.story-text {
- margin: .6em 0;
-}
-.stories dl.collapsible {
- border: 1px solid #ddd;
-}
-
-.booking .listing dl.collapsible {
- border:0 !important;
-}
-
-.stories dl.collapsedBlockCollapsible {
- border:0;
- background-color: transparent;
-}
-
-.booking-details {
- width: 350px;
-}
-
-.booking .listing dl.collapsible dd.collapsibleContent,
-.stories dl.collapsible dd.collapsibleContent {
- margin:0 !important;
-}
-
-.booking .listing dl.collapsible dt.collapsibleHeader {
- background-color: transparent;
- margin-left: 0;
-}
-
-.stories dl.collapsible dt.collapsibleHeader {
- margin-left: 0;
-
-}
-
-/* comments */
-.comments-container {
- margin-bottom: 1em;
-}
-.comments-container .comment {
- font-size: 100%;
- margin-top: .5em;
- border: 1px solid #ddd;
-}
-
-.comment-autor {
- float: left;
- margin: 4px;
- padding: 4px;
- width: 100px;
-}
-
-.comment-text {
- margin-left: 120px;
- padding: 4px;
- border-left: 1px solid #ddd;
- min-height: 50px;
-}
-
-#addstory-container h3 a {
- background: url("./spindown-closed.gif") no-repeat scroll 0 50% transparent;
- padding-left: 1em;
-}
-
-#addstory-container h3 a.open {
- background-image: url('./spindown-open.gif');
-}
-
-#addstory-form {
- padding: 1em;
- border: 1px solid #dddddd;
-}
-
-.tooltip {
- display:none;
- background: white;
- border: 1px solid #dddddd;
- border-radius: 5px;
- height: 25px;
- width: 220px;
- padding: .2em .5em;
- font-size: 85%;
-}
-
-.tooltip a {
- padding: 0 .2em;
- line-height: 25px;
-}
-
-.tooltip .disable {
- color: #dddddd;
-}
-
-#dashboard ul.details {
- font-size: 85%;
- list-style-type: none;
- margin:0;
-}
-
-.more-tasks {
- float: right;
- margin: 1em .2em 0 0;
-}
-
-.simplemanagement-planner ul.sortable {
- min-height: 15px;
-}
-
-.link-parent.visualNoPrint {
- float: right;
-}
View
251 src/abstract/simplemanagement/browser/static/simplemanagement.js
@@ -1,251 +0,0 @@
-(function($) {
- if ((typeof window.simplemanagement) === "undefined") {
- window.simplemanagement = {};
- }
-
- simplemanagement.Planner = function(element) {
- this._init(element);
- };
-
- simplemanagement.Planner.prototype = {
- _init: function(element) {
- var self = this;
- this.element = $(element);
- this.element_id = this.element.attr('id');
- this.has_sortable = false;
- this.left_selector = $('#'+this.element_id+'-left');
- this.right_selector = $('#'+this.element_id+'-right');
- this.loadStories(this.left_selector);
- this.loadStories(this.right_selector);
- this.left_selector.change(function() {
- self.loadStories(self.left_selector);
- });
- this.right_selector.change(function() {
- self.loadStories(self.right_selector);
- });
- },
-
- loadStories: function(selector) {
- var self = this;
- var uuid = $('option:selected', selector).val();
- var container = $('#'+selector.attr('id')+'-container');
- container.load(
- './@@stories',
- {
- iteration: uuid,
- widget_id: this.element_id
- },
- function(response, status, request) {
- self.makeSortable();
- }
- );
- },
-
- makeSortable: function() {
- var self = this;
- if(this.has_sortable) {
- $('ul.sortable', this.element).sortable('destroy');
- this.has_sortable = false;
- }
- $('ul.sortable', this.element).sortable({
- update: function(event, ui) {
- self.update(event, ui);
- },
- start: function(event, ui) {
- if(event.ctrlKey || event.metaKey)
- ui.item.addClass('copy');
- },
- sort: function(event, ui) {
- if(event.ctrlKey || event.metaKey)
- ui.item.addClass('copy');
- else
- ui.item.removeClass('copy');
- },
- stop: function(event, ui) {
- ui.item.removeClass('copy');
- },
- receive: function(event, ui) {
- var destination = $(event.target);
- var destination_uuid = destination.attr('data-uuid');
- var origin = $(ui.sender);
- var origin_uuid = origin.attr('data-uuid');
- if(origin_uuid === destination_uuid) {
- $(ui.sender).sortable('cancel');
- }
- },
- tolerance: 'pointer',
- distance: 5,
- opacity: 0.5,
- revert: true,
- connectWith: '.'+this.element_id+'-stories'
- });
- this.has_sortable = true;
- },
-
- update: function(event, ui) {
- var self = this;
- var destination = $(event.target);
- var destination_uuid = destination.attr('data-uuid');
- var origin = $(ui.sender);
- var origin_uuid = origin.attr('data-uuid');
- var story_id = ui.item.attr('data-storyid');
- if(origin_uuid) {
- if(origin_uuid != destination_uuid) {
- var origin_url = origin.attr('data-moveurl');
- //console.log(story_id+': '+origin_uuid+' -> '+destination_uuid+' @ '+ui.item.index());
- if(origin.children('li').length === 0)
- origin.siblings('p.discreet').show();
- var destination_discreet = destination.siblings(
- 'p.discreet');
- if(destination_discreet.is(':visible'))
- destination_discreet.hide();
- var copy = 'false';
- if(event.ctrlKey || event.metaKey)
- copy = 'true';
- $.getJSON(
- origin_url,
- {
- story_id: story_id,
- new_iteration: destination_uuid,
- new_position: ui.item.index(),
- do_copy: copy
- },
- function(data) {
- if(data['success'] === false) {
- alert(data['error']);
- }
- if(copy) {
- self.loadStories(self.left_selector);
- self.loadStories(self.right_selector);
- }
- }
- );
- }
- }
- else {
- var destination_url = destination.attr('data-moveurl');
- $.getJSON(
- destination_url,
- {
- story_id: story_id,
- new_position: ui.item.index()
- },
- function(data) {
- if(data['success'] === false) {
- alert(data['error']);
- }
- }
- );
- }
- }
- };
-
- simplemanagement.planners = [];
-
- $(document).ready(function(){
-
- $('.simplemanagement-planner').each(function() {
- simplemanagement.planners.push(
- new simplemanagement.Planner(this));
- });
-
- $('.simplemanagement-addstory').each(function() {
- var container = $(this);
- var link = $('a', container);
- var wrapper = $('div.simplemanagement-addstory-form-wrapper');
- if($('dl.portalMessage', wrapper).length > 0){
- wrapper.css('display', 'block');
- add_form_link.addClass('open');
- }
-
- link.bind('click', function(evt) {
- $(this).toggleClass('open');
- evt.preventDefault();
- wrapper.toggle("slow");
- });
- });
-
- $('#overview ul.tabs').tabs("#overview div.panes > div");
-
-
- $('#project-iterations').tabs(
- "#project-iterations div.pane",
- {tabs: 'h2', effect: 'slide', initialIndex: 1}
- );
-
- $('.quickedit').prepOverlay({
- subtype: 'iframe',
- closeselector: '.button-field',
- width:'70%'
- });
-
- $('.story-quickview').prepOverlay({
- subtype: 'ajax',
- filter: common_content_filter,
- formselector: 'form#booking_form',
- width:'80%'
- });
-
- $('.quickview').prepOverlay({
- subtype: 'ajax',
- filter: common_content_filter,
- width:'50%'
- });
-
-
- function updateStoryPosition(event, ui) {
- position = ui.item.index();
- item_id = ui.item.attr('id');
- $.getJSON(
- './story_move?story_id=' + item_id + '&position=' + position,
- function(data){
- if(data['success'] === false) {
- alert(data['error']);
- }
- }
- );
- }
-
- $( ".portaltype-iteration .sortable" ).sortable({
- update: updateStoryPosition,
- tolerance: 'pointer',
- distance: 5,
- opacity: 0.5,
- revert: true
- });
-
-
- $('.status').tooltip({
- events: {
- def: "click,blur"
- },
-
- onBeforeShow: function(){
- var tip = this.getTip();
- tip.empty();
- var trigger = this.getTrigger();
- var url = trigger.attr('href');
- var story_container = $(trigger).parents("li");
- $.get(url + '/wf_actions', function(data) {
- tip.html(data);
- var links = $(tip).find('a');
- links.bind('click', function(evt){
- $.getJSON($(this).attr('href'), function(data) {
- if(data === false) {
- alert('An error occurred');
- }
- else {
- story_container.removeClass(
- 'done in_progress suspended todo');
- story_container.addClass(data);
- tip.hide();
- }
- });
- evt.preventDefault();
- });
- });
- }
- });
-
- });
-})(jQuery);
View
BIN  src/abstract/simplemanagement/browser/static/spindown-closed.gif
Deleted file not rendered
View
BIN  src/abstract/simplemanagement/browser/static/spindown-open.gif
Deleted file not rendered
View
BIN  src/abstract/simplemanagement/browser/static/tag_red.png
Deleted file not rendered
View
73 src/abstract/simplemanagement/browser/story_move.py
@@ -1,73 +0,0 @@
-import json
-import logging
-from Products.Five.browser import BrowserView
-from plone.app.uuid.utils import uuidToObject
-from ..utils import boolize
-
-
-class StoryMove(BrowserView):
-
- def __init__(self, context, request):
- super(StoryMove, self).__init__(context, request)
- self.logger = logging.getLogger('abstract.simplemanagement')
-
- def _get_pasted_id(self, results, story_id):
- new_id = None
- for result in results:
- if result['id'] == story_id:
- new_id = result['new_id']
- if new_id is None:
- raise KeyError(
- "Copy error, original id '%s' not found" % story_id
- )
- return new_id
-
- def move_in_context(self, story_id, new_position, is_copy):
- if is_copy:
- clipboard = self.context.manage_copyObjects(ids=[story_id])
- story_id = self._get_pasted_id(
- self.context.manage_pasteObjects(clipboard),
- story_id
- )
- self.context.moveObject(story_id, int(new_position))
-
- def move_to_iteration(self, new_iteration, story_id, new_position, is_copy):
- if is_copy:
- clipboard = self.context.manage_copyObjects(ids=[story_id])
- else:
- clipboard = self.context.manage_cutObjects(ids=[story_id])
- new_iteration = uuidToObject(new_iteration)
- story_id = self._get_pasted_id(
- new_iteration.manage_pasteObjects(clipboard),
- story_id
- )
- new_iteration.moveObject(story_id, int(new_position))
-
- def process(self):
- story_id = self.request['story_id']
- new_position = self.request['new_position']
- new_iteration = self.request.get('new_iteration', '')
- is_copy = boolize(self.request.get('do_copy', 'false'))
- if not new_position.isdigit():
- raise ValueError("Position '%s' is not a digit" % new_position)
- if not new_iteration:
- self.move_in_context(story_id, new_position, is_copy)
- else:
- self.move_to_iteration(new_iteration, story_id, new_position,
- is_copy)
- return {
- 'success': True,
- 'error': None
- }
-
- def __call__(self):
- try:
- result = self.process()
- except Exception, e: # pylint: disable=W0703
- self.logger.exception("An error occurred while moving the story")
- result = {
- 'success': False,
- 'error': str(e)
- }
- self.request.response.setHeader("Content-type", "application/json")
- return json.dumps(result)
View
142 src/abstract/simplemanagement/browser/templates/dashboard.pt
@@ -1,142 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal"
- xmlns:metal="http://xml.zope.org/namespaces/metal"
- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
- lang="en"
- metal:use-macro="context/main_template/macros/master"
- i18n:domain="plone">
-
-<head>
- <metal:block fill-slot="top_slot"
- tal:define="dummy python:request.set('disable_border',1);
- disable_column_one python:request.set('disable_plone.leftcolumn',1);
- disable_column_two python:request.set('disable_plone.rightcolumn',1);" />
-</head>
-
-<body>
-
-<tal:comment replace="nothing">
- Please note that this template fills the "content" slot instead of the
- "main" slot, this is done so we can provide stuff like the content
- tabs. This also means that we have to supply things that are normally
- present from main_template.
-</tal:comment>
-
-<metal:main fill-slot="content">
- <tal:main-macro metal:define-macro="main"
- tal:define="memberinfo context/portal_membership/getMemberInfo;
- member context/@@plone_portal_state/member;
- name python:memberinfo['fullname'] or member.getId() or member.getId();
- portal_url context/portal_url">
-
-
-
- <div id="edit-bar" tal:condition="view/can_edit">
- <h5 class="hiddenStructure">Views</h5>
-
- <ul id="content-views"
- class="contentViews">
- <li class="selected">
- <a href=""
- tal:attributes="href string:${context/@@plone_portal_state/navigation_root_url}/dashboard"
- i18n:translate="label_dashboard">Dashboard</a>
- </li>
- <li>
- <a href=""
- tal:attributes="href string:${context/@@plone_portal_state/navigation_root_url}/@@manage-dashboard"
- i18n:translate="label_edit">Edit</a>
- </li>
- </ul>
-
- <div class="contentActions" tal:condition="view/can_edit">
- &nbsp;
- </div>
- </div>
-
- <div metal:use-macro="context/global_statusmessage/macros/portal_message">
- Portal status message
- </div>
-<!-- <dl tal:condition="python:view.empty() and view.can_edit()"
- class="portalMessage info visualClear" id="dashboard-info-message">
- <dt i18n:translate="">Info</dt>
- <dd i18n:translate="info_empty_dashboard">
- Your dashboard is currently empty. Click the
- <em>edit</em> tab to assign some personal
- portlets.
- </dd>
- </dl> -->
-
- <div id="content">
-
- <h1 class="documentFirstHeading" i18n:translate="heading_dashboard">
- <span tal:replace="name" i18n:name="user_name" />&#8217;s dashboard
- </h1>
-
- <div id="content-core">
- <div id="dashboard" i18n:domain="abstract.simplemanagement">
- <div class="visualClear"><!-- --></div>
-
- <div class="row" tal:define="dashboard view/dashboard">
- <!-- my stories -->
- <div class="cell width-8 position-0"
- tal:define="stories dashboard/stories;
- show_actions python:True">
- <h3 i18n:translate="">My stories</h3>
- <metal:stories
- use-macro="context/simpemanagement-macros/stories-shortlist" />
-
- <p class="discreet more-tasks"
- tal:condition="dashboard/stories_n">
- <a href=""
- i18n:translate=""
- i18n:attributes="title"
- title="Show more"
- class="quickview"
- tal:attributes="href string:${context/absolute_url}/mystories">
- And <em i18n:name="stories_n" tal:content="dashboard/stories_n">n</em> more</a>
- </p>
- </div>
-
- <!-- my tickets -->
- <div class="cell width-8 position-8"
- tal:define="tickets dashboard/tickets">
- <h3 i18n:translate="">My tickets</h3>
-
- <metal:tickets
- use-macro="context/simpemanagement-macros/tickets-shortlist" />
-
- <p class="discreet more-tasks"
- tal:condition="dashboard/tickets_n">
- <a href=""
- i18n:translate=""
- i18n:attributes="title"
- title="Show more"
- class="quickview"
- tal:attributes="href string:${context/absolute_url}/mytickets">
- And <em i18n:name="ticket_n"
- tal:content="dashboard/tickets_n">n</em> more
- </a>
- </p>
- </div>
- </div><!-- end dashboard -->
-
- <div class="visualClear"><!-- --></div>
-
- <div id="dashboard-portlets1"
- tal:content="structure provider:plone.dashboard1" />
- <div id="dashboard-portlets2"
- tal:content="structure provider:plone.dashboard2" />
- <div id="dashboard-portlets3"
- tal:content="structure provider:plone.dashboard3" />
- <div id="dashboard-portlets4"
- tal:content="structure provider:plone.dashboard4" />
- </div>
- <div class="visualClear"><!-- --></div>
- </div>
- </div>
-
- </tal:main-macro>
-</metal:main>
-
-</body>
-</html>
View
361 src/abstract/simplemanagement/browser/templates/macros.pt
@@ -1,361 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal"
- xmlns:metal="http://xml.zope.org/namespaces/metal"
- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
- lang="en"
- i18n:domain="abstract.simplemanagement"
- tal:omit-tag="">
-
- <metal:iteration define-macro="iteration"
- tal:define="iteration_view nocall:iteration/@@view;
- stories iteration_view/stories;
- totals iteration_view/totals;
- can_edit iteration_view/user_can_edit">
- <h3><a href=""
- title=""
- tal:attributes="href iteration/absolute_url;
- title iteration/title">
- <span tal:content="iteration/title" />
- (<span
- tal:content="python:context.toLocalizedTime(iteration.start.isoformat())"/>&nbsp;-&nbsp;
- <span
- tal:content="python:context.toLocalizedTime(iteration.end.isoformat())" />)
- </a></h3>
- <p tal:content="iteration/Description"></p>
- <metal:stories define-slot="stories">
- <metal:stories-listing
- use-macro="context/simpemanagement-macros/stories" />
- </metal:stories>
- </metal:iteration>
-
- <metal:stories define-macro="stories"
- tal:define="view_details view_details|nothing">
- <ul class=""
- tal:attributes="class python:can_edit and 'stories sortable' or 'stories'"
- tal:condition="stories">
- <li class="story-container"
- tal:repeat="story stories"
- tal:attributes="class string:${story/status} story-container;
- id story/id">
- <div class="story-block clearfix"
- tal:define="assignees story/assignees;
- epic story/epic;
- timing story">
- <metal:timing
- use-macro="context/simpemanagement-macros/story-timing" />
-
- <metal:actions
- use-macro="context/simpemanagement-macros/story-actions" />
-
- <h4>
- <a href=""
- title=""
- tal:attributes="href story/url;
- title story/description"
- tal:content="story/title" />
- </h4>
-
- <p tal:content="story/description"></p>
- <div tal:condition="view_details"
- class="story-text">
- <p tal:replace="structure story/text" />
- </div>
-
- <metal:epic
- use-macro="context/simpemanagement-macros/epic" />
-
- <metal:assignes
- use-macro="context/simpemanagement-macros/assignees" />
-
- <div tal:condition="view_details"
- class="extra-details clearfix">
-
- <dl class="collapsible collapsedOnLoad"
- tal:define="comments story/comments;
- n_comments python:len(comments)">
- <dt class="collapsibleHeader"
- i18n:translate=""><span
- i18n:name="number"
- tal:content="n_comments">0</span> Comments</dt>
- <dd class="collapsibleContent">
- <h5 i18n:translate="">Comments</h5>
- <div class="comments-container clearfix">
- <tal:comments
- repeat="comment_dict comments">
- <div class="comment clearfix" tal:define="depth comment_dict/depth|python:0;
- comment comment_dict/comment"
- tal:attributes="style string:margin-left: ${depth}em">
- <div class="comment-autor">
- <a href=""
- tal:content="comment/Creator">Creator</a>
- <div class="commentDate"
- tal:content="python:context.toLocalizedTime(comment.modification_date.isoformat())">
- 8/23/2001 12:40:44 PM
- </div>
- </div>
- <div class="comment-text"
- tal:content="structure comment/getText"></div>
- </div>
- </tal:comments>
- </div>
- <div class="field">
- <textarea></textarea>
- </div>
- <div class="formActions">
- <button><span i18n:translate="">Send</span></button>
- </div>
- </dd>
- </dl>
- </div>
- </div>
- </li>
- </ul>
-
- <div class="totals clearfix" tal:condition="totals|nothing">
- <table class="timing">
- <tr>
- <th class="totals-label" rowspan="2"
- i18n:translate="">Totals</th>
- <th class="nosort column" i18n:translate="">Estimate</th>
- <th class="nosort column" i18n:translate="">Actual hours</th>
- <th class="nosort column" i18n:translate="">Difference</th>
- </tr>
- <tr>
- <td class="estimate" tal:content="totals/estimate"></td>
- <td class="actual_hours" tal:content="totals/hours"></td>
- <td tal:content="totals/difference"
- tal:attributes="class string:difference ${totals/time_status}">
- </td>
- </tr>
- </table>
- </div>
- <div tal:condition="not:stories">
- <p class="discreet"
- i18n:translate="">
- No stories in this iteration.
- Add a new one or bring them in via the iteration planner.
- </p>
- </div>
-
- </metal:stories>
-
-
- <metal:story-actions define-macro="story-actions">
- <div class="actions"
- tal:condition="show_actions|view_details|nothing">
- <a href=""
- title="View details"
- class="story-quickview"
- i18n:attributes="title"
- tal:attributes="href story/url">
- <span i18n:translate="">Details</span></a>
- <a href=""
- tal:condition="story/can_edit"
- title="Edit story"
- class="quickedit"
- tal:attributes="href python:story['url'] + '/quickedit?ajax_load=1&ajax_include_head=1'"
- i18n:attributes="title">
- <span i18n:translate="">Edit</span></a>
- <a href="#"
- tal:condition="story/can_review"
- title="Change status"
- class="status"
- i18n:attributes="title"
- tal:attributes="href story/url"
- onclick="return false">
- <span tal:content="story/status">Status</span></a>
- </div>
- </metal:story-actions>
-
-
- <metal:assignees define-macro="assignees">
- <div class="assignees-container clearfix">
- <span class="label" i18n:translate="">Assignees</span>
- <ul class="assignees" tal:condition="assignees|nothing">
- <li tal:repeat="assignee assignees">
- <a tal:attributes="href assignee/href"
- tal:content="assignee/fullname" />
- </li>
- </ul>
- <em tal:condition="not:assignees|nothing"
- i18n:translate="">
- Unassigned
- </em>
- </div>
- </metal:assignees>
-
- <metal:epic define-macro="epic">
- <div class="epic-container"
- tal:condition="epic|nothing">
- <span class="label" i18n:translate="">Epic</span>
- <a href=""
- title=""
- tal:attributes="href epic/url;
- title epic/title"
- tal:content="epic/title"></a>
- </div>
- </metal:epic>
-
- <metal:storytiming define-macro="story-timing">
- <table class="timing">
- <tr>
- <th class="nosort column" i18n:translate="">Estimate</th>
- <th class="nosort column" i18n:translate="">Actual hours</th>
- <th class="nosort column" i18n:translate="">Difference</th>
- </tr>
- <tr>
- <td class="estimate" tal:content="timing/estimate"></td>
- <td class="actual_hours" tal:content="timing/resource_time"></td>
- <td tal:content="timing/difference"
- tal:attributes="class string:difference ${timing/time_status}">
- </td>
- </tr>
- </table>
- </metal:storytiming>
-
- <metal:booking define-macro="booking-list">
- <table class="listing"
- tal:condition="booking_list">
- <thead>
- <tr>
- <th i18n:translate="">Date</th>
- <th i18n:translate="">Title</th>
- <th i18n:translate="">Time</th>
- <th i18n:translate="">Assignee</th>
- <th i18n:translate="">Related</th>
- </tr>
- </thead>
- <tbody>
- <tr tal:repeat="booking booking_list">
- <td tal:content="booking/date" />
- <td class="booking-details">
- <h4><a tal:attributes="href booking/href"
- tal:content="booking/title"/>
- </h4>
- <dl class="collapsible collapsedOnLoad"
- tal:condition="booking/description">
- <dt class="collapsibleHeader" i18n:translate="">Details</dt>
- <dd class="collapsibleContent">
- <p class="discreet"
- tal:content="booking/description" />
- </dd>
- </dl>
- </td>
- <td class="actual_hours" tal:content="booking/time" />
- <td><a tal:attributes="href booking/creator/href"
- tal:content="booking/creator/fullname" />
- <td tal:define="related booking/related">
- <span tal:condition="related">
- <a href=""
- tal:attributes="
- href related/href;
- title related/description"
- tal:content="related/title"/>
- </span>
- </td>
- </tr>
- </tbody>
- </table>
- </metal:booking>
-
- <metal:stories-shortlist define-macro="stories-shortlist">
- <ul tal:condition="stories" class="stories">
- <li tal:repeat="story stories"
- tal:attributes="class string:${story/status} story-container">
-
- <metal:actions
- use-macro="context/simpemanagement-macros/story-actions" />
-
- <a href=""
- title=""
- tal:content="story/title"
- tal:attributes="href story/url;
- title story/title"></a>
- <p class="discreet">
- <span tal:replace="story/description" />
- </p>
- <ul class="details">
- <li>
- <span class="label" i18n:translate="">Project:</span>
- <a href="" tal:content="story/project/title"
- tal:attributes="title story/project/description;
- href story/project/url"></a>
- </li>
- <li>
- <span class="label" i18n:translate="">Iteration:</span>
- <a href="" tal:content="story/iteration/title"
- tal:attributes="title story/iteration/description;
- href story/iteration/url"></a>
- </li>
- </ul>
- </li>
- </ul>
- <p class="discreet"
- tal:condition="not:stories"
- i18n:translate="">
- You have no stories assigned
- </p>
- </metal:stories-shortlist>
-
-
- <metal:tickets-shortlist define-macro="tickets-shortlist">
- <ul tal:condition="tickets">
- <li tal:repeat="ticket tickets">
- <a href=""
- title=""
- tal:attributes="href ticket/getURL;
- title ticket/Title">
- <span tal:content="ticket/Title"></span>
- (# <span tal:content="ticket/getId"></span>)
- </a>
- </li>
- </ul>
- <p class="discreet"
- tal:condition="not:tickets"
- i18n:translate="">
- You have no tickets assigned
- </p>
- </metal:tickets-shortlist>
-
- <metal:stories-planning define-macro="stories-planning"
- tal:define="view_details view_details|nothing">
- <p class="discreet"
- tal:attributes="style python:'display:none' if stories else ''"
- i18n:translate="">
- No stories in this iteration.
- Add a new one or bring them in via the iteration planner.
- </p>
- <ul class="stories sortable"
- tal:attributes="class string:${widget_id}-stories stories sortable;
- data-uuid iteration_uuid;
- data-moveurl iteration_moveurl">
- <li class="story-container"
- tal:repeat="story stories"
- tal:attributes="class string:${story/status} story-container;
- data-storyid story/id">
- <div class="story-block clearfix"
- tal:define="assignees story/assignees;
- epic story/epic">
- <h4>
- <a href=""
- title=""
- tal:attributes="href story/url;
- title story/description"
- tal:content="story/title" />
- </h4>
-
- <p tal:content="story/description"></p>
-
- <metal:epic
- use-macro="context/simpemanagement-macros/epic" />
-
- <metal:assignes
- use-macro="context/simpemanagement-macros/assignees" />
-
- </div>
- </li>
- </ul>
-
- </metal:stories-planning>
-
-</html>
View
24 src/abstract/simplemanagement/browser/templates/mystories.pt
@@ -1,24 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal"
- xmlns:metal="http://xml.zope.org/namespaces/metal"
- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
- lang="en"
- metal:use-macro="context/main_template/macros/master"
- i18n:domain="abstract.simplemanagement">
- <body>
-
- <metal:title fill-slot="content-title">
- <h1 i18n:translate="">My stories</h1>
- </metal:title>
-
- <metal:content-core fill-slot="content-core">
- <metal:block define-macro="content-core"
- tal:define="stories view/stories;
- show_actions python:False">
- <metal:stories
- use-macro="context/simpemanagement-macros/stories-shortlist" />
- </metal:block>
- </metal:content-core>
-
- </body>
-</html>
View
23 src/abstract/simplemanagement/browser/templates/mytickets.pt
@@ -1,23 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal"
- xmlns:metal="http://xml.zope.org/namespaces/metal"
- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
- lang="en"
- metal:use-macro="context/main_template/macros/master"
- i18n:domain="abstract.simplemanagement">
- <body>
-
- <metal:title fill-slot="content-title">
- <h1 i18n:translate="">My tickets</h1>
- </metal:title>
-
- <metal:content-core fill-slot="content-core">
- <metal:block define-macro="content-core"
- tal:define="tickets view/tickets">
- <metal:stories
- use-macro="context/simpemanagement-macros/tickets-shortlist" />
- </metal:block>
- </metal:content-core>
-
- </body>
-</html>
View
26 src/abstract/simplemanagement/browser/templates/wf_actions.pt
@@ -1,26 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal"
- xmlns:metal="http://xml.zope.org/namespaces/metal"
- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
- lang="en"
- i18n:domain="abstract.simplemanagement"
- tal:omit-tag="">
- <div class="wf_actions">
- <tal:actions repeat="act view/actions">
- <span tal:condition="not:act/available"
- tal:attributes="title act/description;
- class string:disable ${act/id}">
- <span
- tal:content="act/title"/>
- </span>
- <a href=""
- tal:condition="act/available"
- tal:attributes="title act/description;
- href string:${context/absolute_url}/change_status?action=${act/id}&destination=${act/destination};
- class act/id">
- <span
- tal:content="act/title" />
- </a>
- </tal:actions>
- </div>
-</html>
View
50 src/abstract/simplemanagement/browser/wfactions.py
@@ -1,50 +0,0 @@
-import json
-
-from Products.Five.browser import BrowserView
-from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-from Products.CMFCore.utils import getToolByName
-from Products.CMFCore.WorkflowCore import WorkflowException
-
-
-class WFActions(BrowserView):
-
- wf_actions_template = ViewPageTemplateFile('templates/wf_actions.pt')
-
- @property
- def wf_tool(self):
- return getToolByName(self.context, 'portal_workflow')
-
- def wf_actions(self):
- """return a list of transition available for the context
- """
- actions = {}
- ordered_actions = ['start', 'complete', 'suspend', 'reopen']
- wf = self.wf_tool.getWorkflowsFor(self.context)[0]
-
- for k, v in wf.transitions.items():
- actions[k] = {
- 'id': v.id,
- 'title': v.title,
- 'description': v.description,
- 'available': False,
- 'destination': v.new_state_id
- }
-
- for i in wf.listActionInfos(object=self.context):
- if i['available'] and i['allowed']:
- actions[i['id']]['available'] = True
-
- self.actions = [actions[i] for i in ordered_actions]
- return self.wf_actions_template()
-
- def change_status(self):
- """Perform a workflow transaction
- and return current workflow status"""
- action = self.request.get('action')
- success = self.request.get('destination')
-
- try:
- self.wf_tool.doActionFor(self.context, action)
- except WorkflowException:
- success = False
- return json.dumps(success)
View
34 src/abstract/simplemanagement/configure.py
@@ -1,34 +0,0 @@
-from . import messageFactory as _
-
-TRACKER_ID = 'issues'
-DOCUMENTS_ID = 'documents'
-WARNING_DELTA = 3
-MAN_DAY_HOURS = 8
-WARNING_DELTA_PERCENT = 0.1
-
-STATUS_ITEMS = [
- ('not_started', _(u'Not started')),
- ('analysis', _(u'Analysis')),
- ('offer', _(u'Offer')),
- ('development', _(u'Development')),
- ('staging', _(u'Staging')),
- ('production', _(u'Production')),
- ('maintenance', _(u'Maintenance')),
- ('dead', _(u'Dead'))
-]
-
-ENV_TYPES = [
- ('prototype', _(u'Prototype')),
- ('staging', _(u'Staging')),
- ('production', _(u'Production')),
-]
-
-
-ROLES = [
- ('account', _(u'Account')),
- ('project_manager', _(u'Project manager')),
- ('technical_lead', _(u'Technical lead')),
- ('developer', _(u'Developer')),
- ('designer', _(u'Designer')),
- ('sysadmin', _(u'Sysadmin'))
-]
View
62 src/abstract/simplemanagement/configure.zcml
@@ -1,62 +0,0 @@
-<configure xmlns="http://namespaces.zope.org/zope"
- xmlns:i18n="http://namespaces.zope.org/i18n"
- xmlns:gs="http://namespaces.zope.org/genericsetup"
- xmlns:grok="http://namespaces.zope.org/grok"
- i18n_domain="abstract.simplemanagement">
-
- <includeDependencies package="." />
- <i18n:registerTranslations directory="locales" />
-
- <grok:grok package="." />
- <include package=".browser" />
-
- <subscriber for=".interfaces.IProject
- zope.app.container.interfaces.IObjectAddedEvent"
- handler=".events.create_project_collaterals" />
-
- <subscriber for=".interfaces.IProject
- zope.lifecycleevent.interfaces.IObjectModifiedEvent"
- handler=".events.update_tracker_managers" />
-
- <adapter name="start"