icalviewplugin/0000755000000000000000000000000012740767550012613 5ustar rootrooticalviewplugin/0.11/0000755000000000000000000000000011311403266013152 5ustar rootrooticalviewplugin/0.11/icalview/0000755000000000000000000000000011311403266014755 5ustar rootrooticalviewplugin/0.11/icalview/__init__.py0000755000000000000000000000003211232336504017066 0ustar rootroot# from icalview import * icalviewplugin/0.11/icalview/icalview.py0000755000000000000000000002156511311403266017146 0ustar rootroot""" plugin fro Trac 0.11 to provide iCalendar ouput for ticket's queries author : Xavier Pechoultes Licence: GPL """ import os import sys import re import datetime import time from trac.core import * from trac.util.translation import _ from trac.util.datefmt import format_datetime,localtz,to_datetime from trac.web import IRequestHandler from trac.ticket.query import QueryModule from trac.ticket import Milestone from trac.mimeview.api import IContentConverter from trac.web.chrome import INavigationContributor from cStringIO import StringIO from trac.resource import Resource, get_resource_url from trac.util.text import CRLF class iCalViewPlugin(QueryModule): implements(IRequestHandler, INavigationContributor, IContentConverter) """Plugin class """ def match_request(self, req): return req.path_info == '/ical' def convert_content(self, req, mimetype, query, key): return self.export_ical(req, query) def get_supported_conversions(self): yield ('ical', _('iCalendar'), 'ics', 'trac.ticket.Query', 'text/calendar', 8) def convert_content(self, req, mimetype, query, key): if key == 'ical': return self.export_ical(req, query) def get_active_navigation_item(self, req): return 'iCalendar' def parse_date(self,d): """ parse a user date by using standard or user config formats use `short_date_format` and `date_time_format` config values different formats can be defined separated by `;` """ date_format = self.config['icalendar'].get('short_date_format','%m/%d/%Y') date_time_format = self.config['icalendar'].get('date_time_format','%m/%d/%Y %H:%M') for fmt in date_format.split(";"): try: ts = time.strptime(d, fmt) return datetime.date(ts[0],ts[1],ts[2]) except: self.env.log.debug(d + " not match " + fmt) for fmt in date_time_format.split(";"): try: ts = time.strptime(d, date_time_format) return datetime.datetime(ts[0],ts[1],ts[2],ts[3],ts[4],ts[5],0, localtz) except: self.env.log.debug(d + " not match " + date_time_format) return None def parse_duration(self,d): """ parse a user duration if the duration begin by a 'P', we presume that it math RFC specification for duration the user may set a number of x days (our hours) by ending the duration with a `d` (our `h`) : `2d` (`2h`) we support also standar H:M definition. """ if d[0] == "P" : return d val = re.match("([0-9]{1,})d",d) if val : n_days = int(val.groups()[0]) return datetime.timedelta(n_days+1) val = re.match("([0-9]{1,})h",d) if val : n_hours = int(val.groups()[0]) return datetime.timedelta(0, 0,0,0, 0,n_hours) val = re.match("([0-9]{1,}):([0-9]{2})",d) if val : n_hours = int(val.groups()[0]) n_minutes = int(val.groups()[1]) return datetime.timedelta(0, 0,0,0,n_minutes,n_hours) return datetime.timedelta(1) def format_date(self,content,propname,d,force_date=False,tzinfo=None): if type(d) == datetime.datetime and force_date == False: tz = tzinfo or localtz t = to_datetime(d, tzinfo).astimezone(tz) content.write("%s:%s\r\n" % (propname,t.strftime("%Y%m%dT%H%M%S"))) elif type(d) == datetime.datetime and force_date == True: tz = tzinfo or localtz t = to_datetime(d, tzinfo).astimezone(tz) content.write("%s;VALUE=DATE:%s\r\n" % (propname,t.strftime("%Y%m%d"))) else: content.write("%s;VALUE=DATE:%s\r\n" % (propname,d.strftime("%Y%m%d"))) def export_ical(self, req, query): """ return the icalendar file """ dtstart_key = self.config['icalendar'].get('dtstart','') duration_key = self.config['icalendar'].get('duration','') self.env.log.debug("dtstart_key=%s" % dtstart_key) self.env.log.debug("duration_key=%s" % duration_key) if dtstart_key != '': self.env.log.debug("use dtstart_key=%s" % dtstart_key) if dtstart_key not in query.cols: query.cols.append(dtstart_key) if duration_key != '': if duration_key not in query.cols: query.cols.append(duration_key) if 'priority' not in query.cols: query.cols.append('priority') if 'description' not in query.cols: query.cols.append('description') if 'changetime' not in query.cols: query.cols.append('changetime') if 'time' not in query.cols: query.cols.append('time') query.max = sys.maxint results = query.execute(req) cols = query.get_columns() content = StringIO() content.write('BEGIN:VCALENDAR\r\n') content.write('VERSION:2.0\r\n') content.write('PRODID:2.0:-//Edgewall Software//NONSGML Trac 0.11//EN\r\n') content.write('METHOD:PUBLISH\r\n') content.write('X-WR-CALNAME: test\r\n') attr_map = { "summary" : "SUMMARY", "type" : "CATEGORIES", "description" : "DESCRIPTION" } priority_map = { 'blocker' : 1, 'critical' : 2, 'major' : 6, 'minor' : 8, 'trivial' : 9 } custom_priority_map = self.config['icalendar'].get('priority_map',None) if custom_priority_map != None: for m in custom_priority_map.split(","): k = m.split(":") if len(k) == 2: priority_map[k[0]] = int(k[1]) def write_prop(name, value, params={}): propname = ';'.join([name] + [k + '=' + v for k, v in params.items()]) propvalue = value.replace('\n','\n ').replace('\r\n \r\n','\r\n') content.write("%s:%s\r\n" % (propname,propvalue)) for result in results: ticket = Resource('ticket', result['id']) if 'TICKET_VIEW' in req.perm(ticket): kind = "VEVENT" dtstart = None if dtstart_key != '': dtstart = self.parse_date(result[dtstart_key]) due = None if dtstart == None : kind = "VTODO" self.env.log.debug("is TODO") if result.has_key("milestone"): milestone_key = result["milestone"] if milestone_key != "--" and milestone_key != "": self.env.log.debug("Milestone !" + milestone_key) milestone = Milestone(self.env,milestone_key) due = milestone.due content.write("BEGIN:%s\r\n" % kind) content.write("UID:<%s@%s>\r\n" % (get_resource_url(self.env,ticket,req.href),os.getenv('SERVER_NAME'))) if dtstart != None: self.format_date(content,"DTSTART",dtstart) if duration_key != '': duration = self.parse_duration(result[duration_key]) if type(duration) == datetime.timedelta : content.write("DURATION:P%dDT%dS\r\n" % (duration.days, duration.seconds)) else : content.write("DURATION:%s\r\n" % duration) elif due != None: self.format_date(content,"DUE",due,False) self.format_date(content,"CREATED",result["time"]) self.format_date(content,"DTSTAMP",result["changetime"]) protocol = "http" if os.getenv('SERVER_PROTOCOL') != None : if "HTTPS" in os.getenv('SERVER_PROTOCOL') : protocol = "https" content.write("URL:%s://%s%s\r\n" % (protocol,os.getenv('SERVER_NAME'),get_resource_url(self.env,ticket,req.href))) priority = None try: priority = priority_map[result['priority']] except: self.env.log.debug("unmapped priority %s" % result['priority']) if priority != None: content.write("PRIORITY;VALUE=%s:%d\r\n" % (result['priority'],priority)) for key in attr_map: if key in cols: write_prop(attr_map[key], unicode(result[key]).encode('utf-8'),{}) content.write("END:%s\r\n" % kind) content.write('END:VCALENDAR\r\n') return content.getvalue(), 'text/calendar;charset=utf-8' icalviewplugin/0.11/README.txt0000755000000000000000000000155211232336504014660 0ustar rootroot= icalviewplugin = == Description == '''export ticket queries as icalendar''' Provide iCalendar feeds for ticket queries like [http://trac.edgewall.org/roadmap roadmap module]. It use 2 custom fields for event date and duration (in days). GPL == Installation == The homepage for this plugin is at http://trac-hacks.org/wiki/IcalExporterPlugin with links to the subversion repository and zipped eggs. The plugin is installed in the standard way. For more information, see http://trac.edgewall.org/wiki/TracPlugins == How it Works == The iCanViewPlugin use the Query syntax to provide an iCalendar view for ticket queries. It needs two custom field for etting event date (date_start) and event duration in days (duration). It provide a link in ticket's queries footer and an /ical?.. urls (usefull for specific apache authentication config). icalviewplugin/0.11/setup.py0000755000000000000000000000117711311363310014670 0ustar rootroot# -*- coding: utf-8 -*- from setuptools import find_packages, setup version='0.4' setup(name='icalview', version=version, description="Provide icalendar export for Queries", author='Xavier Péchoultres', author_email='x.pechoulres@clariprint.com', url='http://trac-hacks.org/wiki/IcalViewPlugin', keywords='trac plugin icalendar', license="GPL", packages=find_packages(exclude=['ez_setup', 'examples', 'tests*']), include_package_data=True, zip_safe=False, install_requires=[], entry_points = """ [trac.plugins] icalview = icalview """, )