class ApiDevPanel_ResourceHistory(UUIDView): """ List history of resource """ access = 'is_admin' known_methods = ['GET', 'DELETE'] response_schema = { 'sha': Char_Field(title=MSG(u'SHA of the commit')), 'author_date': Datetime_Field(title=MSG("Datetime of commit")), 'author_name': Char_Field(title=MSG(u"Commit's author name")), 'message_short': Char_Field(title=MSG(u"Commit's title")) } def GET(self, root, context): resource = self.get_resource_from_uuid(context) revisions = resource.get_revisions(content=False) return self.return_json(revisions, context)
class ApiDevPanel_ClassidViewDetails(Api_View): """ Give details about a class_id """ access = 'is_admin' path_query_schema = { 'class_id': Char_Field(title=MSG(u'A class_id registered in DB')) } response_schema = { 'class_title': Char_Field(title=MSG(u'The class_title of the resource cls')), 'class_id': Char_Field(title=MSG(u'The class_id of the resource cls')) } def GET(self, root, context): class_id = context.path_query['class_id'] cls = context.database.get_resource_class(class_id) kw = {'class_title': cls.class_title, 'class_id': class_id} return self.return_json(kw, context)
class Calendar(DBResource): class_id = 'calendar' class_title = MSG(u'Calendar') class_views = ['edit'] # Fields title = DBResource.title(required=True) hidden_for_users = Char_Field(multiple=True) color = Color_Field(title=MSG(u'Color'), default='#0467BA', required=True) owner = Owner_Field # Views _fields = ['title', 'color'] new_instance = Calendar_NewInstance(fields=_fields) edit = Calendar_Edit(fields=_fields)
class Applications_View(AutoTable): title = MSG(u'Applications') base_classes = ('application', ) table_fields = ['checkbox', 'title', 'subscription', 'nb_answers', 'ctime'] table_actions = [Remove_BrowseButton] # Fields nb_answers = Char_Field(title=MSG(u'Nb answers')) def get_item_value(self, resource, context, item, column): if column == 'nb_answers': return item.get_n_forms() elif column == 'ctime': return context.format_datetime(item.get_value('ctime')) # Proxy proxy = super(Applications_View, self) return proxy.get_item_value(resource, context, item, column)
class ApiDevPanel_ServerView(Api_View): """ Return informations about server timestamp / pid / port """ access = 'is_admin' known_methods = ['GET'] response_schema = { 'timestamp': Char_Field(title=MSG(u"Server's start timestamp")), 'pid': Integer_Field(title=MSG(u"Server's PID")), 'port': Integer_Field(title=MSG(u"Server's port")) } def GET(self, root, context): server = context.server kw = {'timestamp': server.timestamp, 'pid': getpid(), 'port': server.port} return self.return_json(kw, context)
class UUIDView(Api_View): """ Base view for all uuid related views """ class_id = None base_class_id = None access = True known_methods = ['DELETE'] path_query_schema = {'uuid': Char_Field(title=MSG(u'The uuid of a resource in DB'))} def is_access_allowed(self, context): # TODO: can be move in itools main view query = get_resource_by_uuid_query( uuid=context.path_query_base['uuid'], base_class_id=self.base_class_id, class_id=self.class_id) search = context.database.search(query) if not search: if context.database.search(query): # Exist but ... # Unauthorized (401) if context.user is None: raise Unauthorized # Forbidden (403) raise Forbidden raise NotFound resource = search.get_resources().next() return context.is_access_allowed(resource, self) def get_resource_from_uuid(self, context): uuid = context.path_query['uuid'] return context.root.get_resource_by_uuid( uuid=uuid, context=context, base_class_id=self.base_class_id, class_id=self.class_id) access_DELETE = 'is_allowed_to_remove' def DELETE(self, resource, context): resource = self.get_resource_from_uuid(context) resource.parent.del_resource(resource.name) return None
class TestValidators(AutoEdit): access = True title = MSG(u"Test validators") fields = [ 'field_1', 'field_2', 'field_3', 'field_4', 'field_5', 'field_6', 'field_7', 'field_8', 'field_9', 'field_10', 'field_11', 'field_12', 'field_13', 'field_14', 'field_15' ] field_1 = Integer_Field( title=MSG(u'5+5 equals to ?'), validators=[validator('equals-to', base_value=10)], error_messages={'not_equals': MSG(u'Give me a 10 ;)')}) field_2 = Char_Field(title=MSG(u'Hexadecimal color'), validators=[validator('hexadecimal')]) field_3 = Integer_Field(title=MSG(u'Give a positive number'), validators=[validator('integer-positive')]) field_4 = Integer_Field( title=MSG(u'Give a strict positive number'), validators=[validator('integer-positive-not-null')]) field_5 = Integer_Field(title=MSG(u'Give a number (max value 10)'), validators=[validator('max-value', max_value=10)]) field_6 = Integer_Field(title=MSG(u'Give a number (min value 10)'), validators=[validator('min-value', min_value=10)]) field_7 = Integer_Field( title=MSG(u'Give a number (>=10 and <=20)'), validators=[validator('min-max-value', min_value=10, max_value=20)]) field_8 = Char_Field(title=MSG(u'Give text (min length: 3 characters)'), validators=[validator('min-length', min_length=3)]) field_9 = Char_Field(title=MSG(u'Give text (max length: 5 characters)'), validators=[validator('max-length', max_length=5)]) field_10 = Email_Field( title=MSG(u'Give an email (unique in DB)'), validators=[validator('unique', field_name='email')], error_messages={ 'invalid': MSG(u'Give be an email address !!!'), 'unique': MSG(u'This address is already used') }) field_11 = File_Field( title=MSG(u'File extension (png)'), validators=[validator('file-extension', allowed_extensions=['png'])]) field_12 = File_Field(title=MSG(u'File mimetypes (image/png)'), validators=[ validator('file-mimetypes', allowed_mimetypes=['image/png']) ]) field_13 = File_Field( title=MSG(u'Image max pixels'), validators=[validator('image-pixels', max_pixels=10 * 10)]) field_14 = Char_Field(title=MSG(u'Strong password'), validators=[validator('strong-password')]) field_15 = Integer_Field(title=MSG(u'Number >=5 and equals to 10'), validators=[ validator('min-value', min_value=5), validator('equals-to', base_value=10), ]) def _get_datatype(self, resource, context, name): field = self.get_field(resource, name) return field(resource=resource) def action(self, resource, context, form): print form
class Event(Content): class_id = 'event' class_title = MSG(u'Event') class_description = MSG(u'Calendar event') class_icon16 = 'icons/16x16/event.png' class_icon48 = 'icons/48x48/event.png' class_views = ['edit', 'links', 'backlinks', 'edit_state', 'subscribe'] # Render render = Event_Render # Fields owner = Owner_Field calendar = Select_Field(datatype=Calendars_Enumerate, required=True, title=MSG(u'Calendar'), indexed=True) dtstart = Start_Field dtend = End_Field place = Char_Field(title=MSG(u'Where')) status = Select_Field(datatype=Status, title=MSG(u'State')) rrule = RRule_Field(title=MSG(u'Recurrence')) exdate = Date_Field(multiple=True) reminder = Reminder_Field(title=MSG(u'Reminder')) uid = Char_Field(readonly=True) def init_resource(self, **kw): super(Event, self).init_resource(**kw) # uid context = get_context() if 'uid' not in kw: uid = '%s@%s' % (self.abspath, context.uri.authority) self.set_value('uid', uid) def get_dates(self): start = self.get_value('dtstart') if type(start) is datetime: start = start.date() end = self.get_value('dtend') if type(end) is datetime: end = end.date() # Recurrence rrule = self.metadata.get_property('rrule') dates = get_dates(start, end, rrule) # Exclude dates exdate = self.get_value('exdate') dates.difference_update(exdate) # Ok return sorted(dates) def get_value(self, name, language=None): if name in ('rrule_interval', 'rrule_byday', 'rrule_until'): f_name, kk, param = name.partition('_') property = self.metadata.get_property(f_name, language=language) if property: value = property.get_parameter(param) if param == 'byday' and value is not None: bydays = [] for v in value: bydays.append(DaysOfWeek.get_name_by_shortname(v)) return bydays return value proxy = super(Event, self) return proxy.get_value(name, language) def get_catalog_values(self): values = super(Event, self).get_catalog_values() values['dates'] = self.get_dates() return values def next_time_event(self): reminder = self.get_value('reminder') if not reminder: return None, None # Get start time (if no time, start_time is midnight) start = self.get_value('dtstart') start_time = start.time() if type(start) is datetime else time(0) # Dates context = get_context() now = context.timestamp delta = timedelta(seconds=reminder) for date in self.get_dates(): date = datetime.combine(date, start_time) reminder = context.fix_tzinfo(date - delta) if reminder > now: return reminder, date return None, None def time_event(self, payload): context = get_context() to_addr = self.get_resource(self.get_owner()).get_value('email') send_email('event-reminder', context, to_addr, event=self, date=payload) def get_ns_event(self, current_day, grid=False): abspath = str(self.abspath) ns = { 'id': abspath, 'link': abspath, 'title': self.get_title(), 'cal': 0, 'color': self.get_color(), 'current_day': current_day, 'description': self.get_value('description'), 'status': self.get_value('status') or 'cal_busy', 'url': None} ############################################################### # URL context = get_context() view = self.get_view('edit') if context.is_access_allowed(self, view, context.user): ns['url'] = '%s/;edit?date=%s' % (abspath, current_day) ############################################################### # Set dtstart and dtend values using '...' for events which # appear into more than one cell dtstart = self.get_value('dtstart') dtend = self.get_value('dtend') dtstart_type = type(dtstart) if dtstart_type is datetime: ns['start'] = Time.encode(dtstart.time())[:5] else: ns['start'] = '00:00' if type(dtend) is datetime: ns['end'] = Time.encode(dtend.time())[:5] else: ns['end'] = '23:59' ############################################################### # Time e_dtstart = dtstart e_dtend = dtend if type(dtstart) is datetime: e_dtstart = dtstart.date() if type(dtend) is datetime: e_dtend = dtend.date() # Does current event occurs on current date ? starts_on = e_dtstart == current_day ends_on = e_dtend == current_day out_on = (e_dtstart < current_day and e_dtend > current_day) ns['TIME'] = None if grid: # Neither a full day event nor a multiple days event if dtstart_type is datetime and dtstart.date() == dtend.date(): if ns['start'] == ns['end']: ns['TIME'] = ns['start'] else: ns['TIME'] = '%s - %s' % (ns['start'], ns['end']) else: ns['start'] = ns['end'] = None elif not out_on: if dtstart_type is datetime: value = '' if starts_on and ns['start'] != ns['end']: value = ns['start'] if ends_on: value = value + '-' else: value = value + '...' if ends_on and ns['start'] != ns['end']: value = value + ns['end'] if not starts_on: value = '...' + value if value: ns['TIME'] = value return ns def get_message(self, context, language=None): # Subject title=self.get_title() subject = MSG(u'The event "{title}" has been modified') subject = subject.gettext(title=title, language=language) # Body message = MSG(u'DO NOT REPLY TO THIS EMAIL.\n\n' u'The user "{last_author}" has made some modifications ' u'to the event "{title}".\n' u'To view these modifications please visit:\n' u'{resource_uri}\n') uri = context.get_link(self) uri = str(context.uri.resolve(uri)) uri += '/;edit' last_author = self.get_value('last_author') last_author = context.root.get_user_title(last_author) body = message.gettext(last_author=last_author, resource_uri=uri, title=title, language=language) # And return return subject, body def get_color(self): calendar = self.get_resource(self.get_value('calendar')) return calendar.get_value('color') # Views new_instance = Event_NewInstance edit = Event_Edit
class ConfigAgenda(Folder): class_id = 'agenda' class_version = '20110606' class_title = MSG(u'Agenda') class_description = MSG(u'Schedule your time with calendar files.') class_icon16 = 'icons/16x16/calendar.png' class_icon48 = 'icons/48x48/calendar.png' class_views = [ 'monthly_view', 'weekly_view', 'daily_view', 'edit_timetables', 'new_calendar', 'import_', 'export_form'] # Configuration config_name = '/agenda' config_group = 'content' # Fields timetables = Char_Field(datatype=Timetables, multiple=True, title=MSG(u'Timetables')) timetables_default = [ (time( 7,0), time( 8,0)), (time( 8,0), time( 9,0)), (time( 9,0), time(10,0)), (time(10,0), time(11,0)), (time(11,0), time(12,0)), (time(12,0), time(13,0)), (time(13,0), time(14,0)), (time(14,0), time(15,0)), (time(15,0), time(16,0)), (time(16,0), time(17,0)), (time(17,0), time(18,0)), (time(18,0), time(19,0)), (time(19,0), time(20,0)), (time(20,0), time(21,0))] def init_resource(self, **kw): super(ConfigAgenda, self).init_resource(**kw) # Create default calendar kw = {'title': {'en': u'My events'}, 'color': '#AC81A1'} self.make_resource(None, Calendar, **kw) def get_timetables(self): """Build a list of timetables represented as tuples(start, end). Data are taken from metadata or from class value. Example of metadata: <timetables>(8,0),(10,0);(10,30),(12,0);(13,30),(17,30)</timetables> """ timetables = self.get_value('timetables') if timetables: return timetables # From class value return self.timetables_default def get_document_types(self): return [Calendar, Event] ####################################################################### # User Interface ####################################################################### def to_ical(self, context): """Serialize as an ical file, generally named .ics """ lines = ['BEGIN:VCALENDAR\n', 'VERSION:2.0\n', 'PRODID:-//itaapy.com/NONSGML ikaaro icalendar V1.0//EN\n'] # Calendar components for event in self.search_resources(cls=Event): lines.append('BEGIN:VEVENT\n') for ikaaro_name, ics_name in ikaaro_to_ics: property = event.get_property(ikaaro_name) lang = property.get_parameter('lang') if lang: property = Property(property.value, LANGUAGE=lang) p_schema = {'LANGUAGE': String(multiple=False)} else: p_schema = None datatype = event.get_field(ikaaro_name).datatype line = property_to_str(ics_name, property, datatype, p_schema) lines.append(line) lines.append('END:VEVENT\n') lines.append('END:VCALENDAR\n') return ''.join(lines) def parse_ical(self, data): """Parse iCal data to produce a sequence of tuples: name, value {param_name: param_value} Where all the elements ('name', 'value', 'param_name' and 'param_value') are byte strings. Only elements that are handled by Ikaaro are keeped """ unfolded = unfold_lines(data) iterator = iter(unfolded) for line in iterator: parameters = {} name, line = read_name(line) # Read the parameters and the property value value, parameters = get_tokens(line) if name == 'BEGIN' and value not in ('VCALENDAR', 'VEVENT'): while get_tokens(read_name(iterator.next())[1])[0] != value: pass continue else: yield name, value, parameters def load_state_from_ical_file(self, file): # Clear Calendar for event in self._get_names(): self.del_resource(event) ical = iCalendar() ical.reset() ical._load_state_from_file(file) timezones = ical.get_components('VTIMEZONE') events = ical.get_components('VEVENT') i = 0 for event in events: filename = str(i) properties = {} for name, value in event.get_property().items(): if name in ics_to_ikaaro: name = ics_to_ikaaro[name] properties[name] = value.value properties['uid'] = event.uid self.make_resource(filename, Event, **properties) i += 1 # Views monthly_view = MonthlyView weekly_view = WeeklyView daily_view = DailyView new_event = Calendar_NewEvent edit_timetables = TimetablesForm export = Calendar_Export import_ = Calendar_Import export_form = Calendar_ExportForm calendars = Calendars_View new_calendar = NewResource_Local(title=MSG(u'Add calendar'))
class Application(Folder): class_id = 'application' class_title = MSG(u"Collection Application") class_description = MSG(u"Create from an OpenDocument Spreadsheet file") class_views = ['view_admin', 'edit', 'edit_ods', 'register', 'view'] # FIXME Configuration obsolete ? allowed_users = 10 # Fields subscription = Subscription_Field(required=True) data = File_Field(title=MSG(u'ODS file'), multilingual=False, required=True) filename = Char_Field() mimetype = Char_Field() def init_resource(self, *args, **kw): proxy = super(Application, self) proxy.init_resource(*args, **kw) # Initial forms answers containers self.make_resource('forms', Forms) def load_ods_file(self, data, context): if self.get_resource('model', soft=True): self.del_resource('model') context.database.save_changes() # Create model container self.make_resource('model', FormModel) model = self.get_resource('model') # Save informations filename, mimetype, body = data self.set_value('data', body) self.set_value('filename', filename) self.set_value('mimetype', mimetype) # Analyse it return model.load_ods_file(context) def get_form(self): # OBSOLETE METHOD FIXME raise NotImplementedError def search_forms(self): query = AndQuery(PhraseQuery('base_classes', 'form'), get_base_path_query(self.abspath)) return get_context().search(query) def get_forms(self): return self.search_forms().get_resources() def get_n_forms(self): return len(self.search_forms()) def get_stats(self): stats = {} stats['registered_users'] = 0 stats['unconfirmed_users'] = 0 stats['empty_forms'] = 0 stats['pending_forms'] = 0 stats['finished_forms'] = 0 users = self.get_resource('/users') for form in self.get_forms(): stats['registered_users'] += 1 user = users.get_resource(form.name, soft=True) if user is not None: if user.get_value('password') is None: stats['unconfirmed_users'] += 1 else: state = form.get_value('form_state') if state == EMPTY: stats['empty_forms'] += 1 elif state == PENDING: stats['pending_forms'] += 1 elif state == FINISHED: stats['finished_forms'] += 1 return stats def get_param_folder(self): return self def get_admin_url(self, context): base_url = context.uri.resolve(self.abspath) return base_url.resolve2(';view') def get_spread_url(self, context, email=None): base_url = context.uri.resolve(self.abspath) spread_url = base_url.resolve2(';login') if email is not None: spread_url.query['username'] = email return spread_url def subscribe_user(self, user): root = self.get_resource('/') username = user.name # Give the role "guests" to see public resources (logo, etc.) if (root.get_user_role(username) is None # Except to top-level admins and not root.is_admin(user, self)): root.set_user_role(username, 'guests') # Add the form if self.get_resource(username, soft=True) is None: self.make_resource(username, Form, title={'en': user.get_title()}, form_state=NOT_REGISTERED) # Views new_instance = Application_NewInstance() edit = Application_Edit() edit_ods = Application_EditODS() view_admin = IconsView() register = Application_Register()