""" qp/fill/stored_file.qpy """ from durus.utils import BytesIO from os.path import exists from qp.fill.directory import Directory from qp.fill.form import Form from qp.fill.html import div, href, img, htmltag, span from qp.fill.static import FileStream from qp.fill.static import FileStream from qp.fill.widget import FileWidget, CompositeWidget, SingleSelectWidget, TextWidget from qp.http.request import Upload from qp.http.response import formatdate from qp.lib.stored_file import new_file from qp.lib.util import randbytes from qp.pub.common import format_date_time from qp.pub.common import get_user, header, footer, redirect, get_response, not_found from qp.pub.common import not_found, get_request, get_response import os, sys import mimetypes mimetypes.add_type('text/plain', '.py') def guess_mime_type(name, fallback=''): return mimetypes.guess_type(name.lower())[0] or fallback thumbnail_types = ['image/png', 'image/jpeg', 'image/gif'] try: from PIL import Image except ImportError: Image = None print("no PIL") def thumbnail_response(fp, size=50, cache_time=86400, mime_type="image/png", last_modified=None): if Image: try: image = Image.open(fp) image.load() except IOError: not_found() # Scale down the image query = get_request().get_query() thumbnail_size = size if query: try: thumbnail_size = int(query) except ValueError: pass if thumbnail_size < 2: not_found() try: image.thumbnail((thumbnail_size, thumbnail_size)) except IOError: not_found() output = BytesIO() try: image.save(output, 'PNG', **image.info) mime_type = "image/png" except IOError: not_found() else: output = None response = get_response() response.set_header("Cache-Control", "public, max-age=%s" % cache_time) response.set_expires(seconds=cache_time) if last_modified: response.set_header("Last-Modified", last_modified) response.set_content_type(mime_type, None) if output: return output.getvalue() else: return FileStream(fp) def thumbnail_response_for_file(path, size=50, cache_time=86400, mime_type="image/png"): try: stat = os.stat(path) except OSError: print(sys.exc_info()[1]) not_found() last_modified = formatdate(stat.st_mtime) if last_modified == get_request().get_header('If-Modified-Since'): get_response().set_status(304) return '' return thumbnail_response(open(path), size=size, cache_time=cache_time, mime_type=mime_type, last_modified=last_modified) def get_filename(upload): if isinstance(upload, Upload): return upload.base_filename else: return None def get_mimetype(upload): return guess_mime_type(get_filename(upload), 'application/octet-stream') def get_stored_file(upload, description=None): if upload is None: return None stored_file = new_file(upload) stored_file.set_mime_type(get_mimetype(upload)) stored_file.set_filename(get_filename(upload)) stored_file.set_owner(get_user()) stored_file.set_description(description) return stored_file def format_stored_file:xml(stored_file, download_url=None, show_description=True): classes = ["stored_file"] if stored_file.get_hidden(): classes.append("hidden_stored_file") if show_description: description = div(stored_file.get_description(), css_class="stored_file_description") else: description = None div(href(download_url, stored_file.get_filename(), css_class="stored_file_link"), div(' (%s, %s) ' % ( format_file_size(stored_file.get_size()), stored_file.get_mime_type() or guess_mime_type(stored_file.get_filename(), 'application/octet-stream')), css_class="stored_file_details"), div(format_date_time(stored_file.get_date()), css_class="stored_file_date"), description, classes=classes) def format_stored_file_icon:xml(stored_file, icon_url=None): classes = ['stored_file_icon'] if stored_file: mime_type = stored_file.get_mime_type() or guess_mime_type(stored_file.get_filename(), 'application/octet-stream') mime_type_class = mime_type.replace('.', '_').replace('/', '_') classes.append(mime_type_class) if icon_url: classes.append('img_container') div(icon_url and img(icon_url), classes=classes) class StoredFileWidget (CompositeWidget): keep = 'Keep' replace = 'Replace' delete = 'Delete' hide = 'Hide' unhide = 'Unhide' def __init__(self, name, hint=None, title=None, download_url=None, show_description=False, icon_url=None, mime_types=None, path_prefix='', **kwargs): CompositeWidget.__init__(self, name, title=title, **kwargs) self.download_url = download_url self.icon_url = icon_url if self.value : if self.download_url is None: self.download_url = path_prefix + default_download_url(self.value) if self.icon_url is None and self.value.get_mime_type() in thumbnail_types: self.icon_url = path_prefix + default_icon_url(self.value) self.show_description = show_description if mime_types is not None: assert type(mime_types) is list self.mime_types = mime_types self.file_widget_id = randbytes(10) if self.icon_url: self.icon_url += '?%s' % self.file_widget_id # Make sure that the version displayed is current. if self.value is not None: js = "if (this.value=='%s'){document.getElementById('%s').style.visibility='visible'}else{document.getElementById('%s').style.visibility='hidden'}" % ( self.replace, self.file_widget_id, self.file_widget_id) options = [self.keep, self.replace, self.delete] if self.value.get_hidden(): options.append(self.unhide) else: options.append(self.hide) self.add(SingleSelectWidget, 'action', options=options, value=self.keep, onchange=js) self.add(FileWidget, 'new_file', hint=hint) if show_description: self.add(TextWidget, 'description', rows=2, cols=30, value=self.value.get_description(), title="Description") else: self.add(FileWidget, 'new_file', hint=hint) if show_description: self.add(TextWidget, 'description', rows=2, cols=30, title="Description") def render_icon:xml(self): format_stored_file_icon(self.value, icon_url=self.icon_url) def render_stored_file:xml(self): format_stored_file(self.value, download_url=self.download_url, show_description=self.show_description) def render_content:xml(self): if self.show_description: description = self.get_widget('description').render() else: description = None if self.value: self.render_icon() self.render_stored_file() div(self.get_widget('action') and self.get_widget('action').render(), div(self.get_widget('new_file').render(), description, id=self.file_widget_id, css_class="file_widget_container"), css_class="stored_file_widgets") else: div(self.get_widget('new_file').render(), description, css_class="new_stored_file") def _parse(self, request=None): if not self.has_error(): if self.get('action') == self.delete: self.value = None elif self.get('new_file'): if self.mime_types is not None: mime_type = get_mimetype(self.get('new_file')) if mime_type not in self.mime_types: error = 'Wrong type of file: %s. \n' % mime_type if len(self.mime_types) == 1: error += 'Expected %s' % self.mime_types[0] self.set_error(error) return self.value = get_stored_file(self.get('new_file')) self.value.create_easy_variants() # split pdfs. if self.show_description: self.value.set_description(self.get('description')) if self.get('action') == self.replace: self.get_widget('action').value = self.keep if self.value and self.show_description: self.value.set_description(self.get('description')) if self.value and self.get('action') == self.hide: self.value.set_hidden(True) self.get_widget('action') == self.keep if self.value and self.get('action') == self.unhide: self.value.set_hidden(False) self.get_widget('action') == self.keep class StoredFileListWidget (CompositeWidget): def __init__(self, name, title=None, new=1, extra=5, add_more="Add More", value=None, stored_file_widget=StoredFileWidget, **kwargs): CompositeWidget.__init__(self, name, title=title, value=value) self.new = new if new == 0 and not self.value: new = 1 self.extra = extra self.add_more = add_more if self.value: for j, stored_file in enumerate(self.value): self.add(stored_file_widget, "old%s" % j, value=stored_file, **kwargs) for j in range(0, new): self.add(stored_file_widget, "new%s" % j, **kwargs) for j in range(0, extra): self.add(stored_file_widget, "extra%s" % j, **kwargs) self.extra_widgets = self.get_widgets()[-extra:] def render_content:xml(self): for widget in self.get_widgets(): if widget not in self.extra_widgets: widget.render() extra_id = randbytes(10) add_more_id = randbytes(10) for widget in self.extra_widgets: if widget.has_error(): extra_widget_style = "display:block" break else: extra_widget_style = "display:none" js = "genlib.display_block('%s');genlib.display_none('%s')" % (extra_id, add_more_id) span(self.add_more, css_class="add_more_stored_files", onclick=js, id=add_more_id) htmltag('div', id=extra_id, css_class="extra_stored_file_widgets", style=extra_widget_style) for widget in self.extra_widgets: widget.render() '' def _parse(self, request=None): CompositeWidget._parse(self, request=request) self.value = [w.value for w in self.get_widgets() if w.value is not None] class StoredFileListDirectory (Directory): def __init__(self, stored_file_list, title="Files", title2=None, manage=True, stored_file_widget=None, stored_file_ui=None): self.stored_file_list = stored_file_list self.title = title self.title2 = title2 self.manage = manage self.stored_file_widget = stored_file_widget or StoredFileWidget self.stored_file_ui = stored_file_ui or StoredFileUI def get_exports(self): yield ('', 'index', 'View', None) if self.manage: if len(self.stored_file_list) > 1: yield ('order', 'order', 'Order', None) yield ('edit', 'edit', 'Edit', None) yield ('move', 'move', None, None) def move:xml(self): form = Form(method="get") form.add_int('from', title="From Position") form.add_int('to', title="To Position") if form.get('from') and form.get('to'): stored_file = self.stored_file_list[form.get('from') - 1] self.stored_file_list.remove(stored_file) self.stored_file_list.insert(form.get('to') - 1, stored_file) redirect('order') header('Move') form.render() footer() def index:xml(self): header(self.title) self.title2 stored_file_list = [stored_file for stored_file in self.stored_file_list if not stored_file.get_hidden()] n = len(stored_file_list) if n: for j, stored_file in enumerate(stored_file_list): div(format_stored_file_icon(stored_file, icon_url=default_icon_url(stored_file)), format_stored_file(stored_file, show_description=True, download_url=default_download_url(stored_file)), div(style="clear:both"), css_class="stored_file_index_item") else: div('There are no files in this list.') footer() def order:xml(self): header(self.title) self.title2 div("Change Order", css_class="stored_file_title3") n = len(self.stored_file_list) if n: for j, stored_file in enumerate(self.stored_file_list): if j < n - 1: down = href('move?from=%s&to=%s' % (j + 1, j + 2), '↓', css_class="button") else: down = '' if j > 0: up = href('move?from=%s&to=%s' % (j + 1, j), '↑', css_class="button") else: up = '' div(div(up, down, css_class="stored_file_controls"), format_stored_file_icon(stored_file, icon_url=default_icon_url(stored_file)), format_stored_file(stored_file, show_description=True, download_url=default_download_url(stored_file)), div(style="clear:both"), css_class="stored_file_index_item") else: div('There are no files in this list.') footer() def edit:xml(self): form = Form(enctype='multipart/form-data', id="stored_file_list_directory_edit") form.add(StoredFileListWidget, 'stored_files', value=self.stored_file_list, show_description=True, stored_file_widget=self.stored_file_widget) form.add_submit('save', 'Save') form.add_submit('cancel', 'Cancel') if form.get('cancel'): redirect('.') if form.get('save') and not form.has_errors(): self.stored_file_list[:] = form.get('stored_files') header('Edit %s' % self.title) self.title2 div("Edit List", css_class="stored_file_title3") form.render() footer() def _q_lookup(self, component): for stored_file in self.stored_file_list: if component in stored_file.get_ids(): return self.stored_file_ui(stored_file) for stored_file in self.stored_file_list: if stored_file.get_filename() == component: return self.stored_file_ui(stored_file) def return_file(path=None, filename=None, mime_type=None, charset=None, disposition=None): try: fp = open(path, 'rb') size = os.stat(path).st_size except IOError: print("\nCOULD NOT OPEN %s" % path) not_found() response = get_response() #response.set_expires(seconds=self.cache_time) if mime_type is None and filename: mime_type = guess_mime_type(filename, 'application/octet-stream') response.set_content_type(mime_type, charset) response.set_header('Cache-Control', 'public') if disposition is None: if mime_type == 'text/html': disposition = 'attachment' else: disposition = 'inline' response.set_header( 'Content-Disposition', '%s; filename="%s"' % (disposition, filename)) return FileStream(fp, length=size) def return_stored_file(stored_file): if stored_file is None: not_found() return return_file( path=stored_file.get_full_path(), filename=stored_file.get_filename(), mime_type=stored_file.get_mime_type()) def format_pages:xml(stored_file, stored_file_ui_path='', **kwargs): if stored_file.is_pdf_with_png_pages(): htmltag('div', **kwargs) for page in stored_file.list_existing_png_pages(): image_path = "%s%s" % (stored_file_ui_path, page) img(src=image_path, alt=page, href=image_path) '' class StoredFileUI (Directory): def __init__(self, stored_file): self.stored_file = stored_file def get_exports(self): yield ('', 'index', None, None) yield ('thumbnail', 'thumbnail', None, None) yield ('view_pages', 'view_pages', None, None) def index:xml(self): return return_stored_file(self.stored_file) def thumbnail(self): if self.stored_file.is_pdf_with_png_pages(): image_path = self.stored_file.get_png_page_path(1) elif self.stored_file.get_mime_type() in thumbnail_types: image_path = self.stored_file.get_full_path() return thumbnail_response_for_file(image_path, size=200) def view_pages:xml(self): header(self.stored_file.get_filename()) format_pages(self.stored_file, css_class="view_pages") footer() def _q_lookup(self, component): if '..' in component or '/' in component: return None path = self.stored_file.get_full_path() if "." in component: path_with_component = path + component filename = self.stored_file.get_filename() + component else: path_with_component = path + "." + component filename = self.stored_file.get_filename() + "." + component if exists(path_with_component): return return_file(path=path_with_component, filename=filename) def default_icon_url(stored_file): if stored_file and stored_file.get_mime_type() in thumbnail_types: return stored_file.get_id() + '/thumbnail' def default_download_url(stored_file): if stored_file: return stored_file.get_id() + '/' def format_file_size(size): if size is None: return 'file missing' elif size > 1024*1024: return '%.1f MB' % (size/1024.0/1024) elif size > 4096: return '%.1f KB' % (size/1024.0) else: return ('%i bytes') % size