def delete(self, key, confirm='yes'): c = self.collection c.user.authorize('delete', c) if confirm == 'no': record = locate(c, key) if record: c.user.authorize('delete', record) self.before_delete(record) c.store.delete(record) c.search_engine(c).delete( record._id, ) self.after_delete(record) msg = '%s deleted %s %s' % ( c.user.link, c.link, record.name ) logger = logging.getLogger(__name__) logger.info(msg) log_activity(msg) return redirect_to(c.url)
def edit(self, key, **data): """Display an edit form for a record""" c = self.collection user = c.user user.authorize('update', c) record = locate(c, key) if record: user.authorize('read', record) user.authorize('update', record) c.fields.initialize(record) c.fields.update(data) form = form_for(c.fields, ButtonField('Save', cancel=record.url)) if c.verbose: msg = '%s edited %s %s' % ( user.link, c.link, record.link, ) log_activity(msg) return page(form, title=c.item_title) else: return page('%s missing' % key)
def create_button(self, *args, **data): """Create a record""" collection = self.collection user = collection.user if collection.fields.validate(data): record = collection.model( collection.fields, ) record.pop('key', None) try: key = record.key except AttributeError: key = None if key and locate(collection, record.key) is not None: error('That {} already exists'.format(collection.item_name)) else: try: # convert property to data attribute # so it gets stored as data record.key = record.key except AttributeError: # can happen when key depends on database # auto-increment value. pass record.update(dict( created=now(), updated=now(), owner_id=user._id, created_by=user._id, updated_by=user._id, )) self.before_insert(record) collection.store.put(record) collection.search_engine(collection).add( record._id, collection.fields.as_searchable(), ) self.after_insert(record) msg = '%s added %s %s' % ( user.link, collection.link, record.link ) logger = logging.getLogger(__name__) logger.info(msg) log_activity(msg) return redirect_to(collection.url)
def add_image(self, *_, **kwargs): """accept uploaded images and attach them to the record""" dummy = zoom.Record( filename='dummy.png', file=io.StringIO('test'), ) # put the uploaded image data in a bucket path = os.path.join(zoom.system.site.data_path, 'buckets') bucket = Bucket(path) f = kwargs.get('file', dummy) name = f.filename data = f.file.read() item_id = bucket.put(data) # create an image record image = Image( image_id=item_id, image_size=len(data), image_name=name, draft=True, ) images = zoom.store.store_of(Image) images.put(image) log_activity('%s uploaded image %s' % (zoom.system.user.link, image.access_link)) return item_id
def save_button(self, key, *a, **data): """Save a record""" collection = self.collection user = collection.user user.authorize('update', collection) if collection.fields.validate(data): record = locate(collection, key) if record: user.authorize('update', record) record.update(collection.fields) record.pop('key', None) if record.key != key and locate(collection, record.key): # record key should always be a str, even if the actual # record.id is being used as the key. error('That {} already exists'.format(collection.item_name)) else: record.updated = now() record.updated_by = user._id # convert property to data attribute # so it gets stored as data record.key = record.key self.before_update(record) collection.store.put(record) collection.search_engine(collection).update( record._id, collection.fields.as_searchable(), ) self.after_update(record) msg = '%s updated %s %s' % ( user.link, collection.link, record.link ) logger = logging.getLogger(__name__) logger.info(msg) log_activity(msg) if record.key != key: log_activity( '%s changed %s %s to %s' % ( user.link, collection.link, key, record.key ) ) return redirect_to(record.url)
def show(self, key): """Show a record""" def action_for(record, name): return name, '/'.join([record.url, id_for(name)]) c = self.collection user = c.user record = locate(c, key) if record: user.authorize('read', record) actions = [] if user.can('delete', record): actions.append(action_for(record, 'Delete')) if user.can('update', record): actions.append(action_for(record, 'Edit')) if user.can('create', record): actions.append(action_for(record, 'New')) c.fields.initialize(c.model(record)) if 'updated' in record and 'updated_by' in record: memo = ( '<div class="meta" style="float:right">' 'updated %(when)s by %(who)s' '</div>' '<div style="clear:both"></div>' ) % dict( when=zoom.helpers.when(record['updated']), who=zoom.helpers.who(record['updated_by']) ) else: memo = '' if c.verbose: msg = '%s viewed %s %s' % ( user.link, c.link, record.link, ) log_activity(msg) return page( c.fields.show() + memo, title=c.item_title, actions=actions )
def delete(self, *route, **req_data): """Handle a file delete.""" # Read the file ID from the request, with safety. try: file_id = UUID(req_data['file_id']).hex except ValueError: return Response(status='400 Bad Request') # Retrieve and delete the file. stored_files = StoredFile.collection() to_delete = stored_files.first(id=file_id) log_activity('%s deleted file %s' % (context.user.link, to_delete.filename)) stored_files.delete(to_delete) get_bucket().delete(to_delete.data_id) return Response(status='200 OK')
def index(self, q='', *args, **kwargs): """collection landing page""" def matches(item, search_text): """match a search by field values""" terms = search_text and search_text.split() fields.update(item) v = repr(fields.display_value()).lower() return terms and not any(t.lower() not in v for t in terms) c = self.collection user = c.user fields = c.fields if c.request.route[-1:] == ['index']: return redirect_to('/' + '/'.join(c.request.route[:-1]), **kwargs) actions = user.can('create', c) and ['New'] or [] authorized = (i for i in c.store if user.can('read', i)) matching = (i for i in authorized if not q or matches(i, q)) filtered = c.filter and filter(c.filter, matching) or matching items = sorted(filtered, key=c.order) if q: msg = '%s searched %s with %r (%d found)' % (user.link, c.link, q, len(items)) log_activity(msg) if len(items) != 1: footer_name = c.title else: footer_name = c.item_name footer = '%s %s' % (len(items), footer_name.lower()) content = browse([c.model(i) for i in items], labels=c.labels, columns=c.columns, fields=c.fields, footer=footer) return page(content, title=c.title, actions=actions, search=q)
def remove_image(self, *_, **kwargs): """remove a dropzone image""" # k contains item_id and filename for file to be removed item_id = kwargs.get('id', None) # detach the image from the record if item_id: images = zoom.store.store_of(Image) key = images.first(image_id=item_id) log_activity('%s deleted image %s' % (zoom.system.user.link, key.image_name)) if key: images.delete(key) # delete the bucket path = os.path.join(zoom.system.site.data_path, 'buckets') bucket = Bucket(path) if item_id in bucket.keys(): bucket.delete(item_id) return 'ok' return 'empty'
def upload(self, *route, **req_data): """Handle a file upload.""" # Read the FieldStorage. file_desc = req_data['file'] file_mimetype = req_data['mimetype'] if not isinstance(file_desc, FieldStorage): # Python is dangerous when the type is incorrectly assumed. return Response(b'invalid request body', status='400 Bad Request') # Persist the file. data_id = get_bucket().put(file_desc.value) to_store = StoredFile(id=uuid4().hex, data_id=data_id, mimetype=file_mimetype, content_length=len(file_desc.value), original_name=file_desc.filename) StoredFile.collection().put(to_store) log_activity('%s uploaded file %s' % (context.user.link, to_store.access_link)) # Respond. return Response(bytes(to_store.access_url, 'utf-8'), status='201 Created')
def index(self, q='', *args, **kwargs): """collection landing page""" def get_recent(number): cmd = """ select row_id, max(value) as newest from attributes where kind = %s and attribute in ("created", "updated") group by row_id order by newest desc limit %s """ ids = [id for id, _ in c.store.db(cmd, c.store.kind, number)] return c.store.get(ids) c = self.collection user = c.user if c.request.route[-1:] == ['index']: return redirect_to('/'+'/'.join(c.request.route[:-1]), **kwargs) actions = user.can('create', c) and ['New'] or [] logger = logging.getLogger(__name__) if q: title = 'Selected ' + c.title records = c.search_engine(c).search(q) else: many_records = bool(len(c.store) > 50) logger.debug('many records: %r', many_records) if many_records and not kwargs.get('all'): title = 'Most Recently Updated ' + c.title records = get_recent(15) actions.append(('Show All', 'clients?all=1')) else: title = c.title records = c.store authorized = (i for i in records if user.can('read', i)) filtered = c.filter and filter(c.filter, authorized) or authorized items = sorted(filtered, key=c.order) num_items = len(items) if num_items != 1: footer_name = c.title.lower() else: footer_name = c.item_title.lower() if q: msg = '%s searched %s with %r (%d found)' % ( user.link, c.link, q, num_items ) log_activity(msg) footer = '{:,} {} found in search of {:,} {}'.format( num_items, footer_name, len(c.store), c.title.lower(), ) else: if many_records: footer = '{:,} {} shown of {:,} {}'.format( num_items, footer_name, len(c.store), c.title.lower(), ) else: footer = '%s %s' % (len(items), footer_name) content = browse( [c.model(i) for i in items], labels=c.get_labels(), columns=c.get_columns(), fields=c.fields, footer=footer ) return page(content, title=title, actions=actions, search=q)
def index(self, q='', *args, **kwargs): """collection landing page""" c = self.collection user = c.user if c.request.route[-1:] == ['index']: return redirect_to('/' + '/'.join(c.request.route[:-1]), **kwargs) actions = user.can('create', c) and ['New'] or [] logger = logging.getLogger(__name__) if q: title = 'Selected ' + c.title records = c.search(q) else: has_many_records = c.has_many_records logger.debug('has many records: %r', has_many_records) if has_many_records and not kwargs.get('all'): title = 'Most Recently Updated ' + c.title records = self._get_recent(15) actions.append(('Show All', c.url + '?all=1')) else: title = c.title records = c.store authorized = (i for i in records if user.can('read', i)) filtered = c.filter and filter(c.filter, authorized) or authorized items = sorted(filtered, key=c.order) items = c.sorter and c.sorter(items) or items num_items = len(items) if num_items != 1: footer_name = c.title.lower() else: footer_name = c.item_title.lower() if q: msg = '%s searched %s with %r (%d found)' % (user.link, c.link, q, num_items) log_activity(msg) footer = '{:,} {} found in search of {:,} {}'.format( num_items, footer_name, len(c.store), c.title.lower(), ) else: if has_many_records: footer = '{:,} {} shown of {:,} {}'.format( num_items, footer_name, len(c.store), c.title.lower(), ) else: footer = '%s %s' % (len(items), footer_name) content = browse([c.model(i) for i in items], labels=c.get_labels(), columns=c.get_columns(), fields=c.fields, footer=footer) return page(content, title=title, actions=actions, search=q)