def attach_project_pictures(project, api): """Utility function that queries for file objects referenced in picture header and square. In eve we currently can't embed objects in nested properties, this is the reason why this exists. This function should be moved in the API, attached to a new Project object. """ if project.properties.picture_square: # Collect the picture square file object project.properties.picture_square = File.find( project.properties.picture_square, api=api) if project.properties.picture_header: # Collect the picture header file object project.properties.picture_header = File.find( project.properties.picture_header, api=api)
def item_delete(item_id): """We run this when updating a preview picture (after succesfully uploading a new one). """ api = system_util.pillar_api() try: file_item = File.find(item_id, api=api) file_item.delete(api=api) message = 'File deleted' except ResourceNotFound: message = 'File not found' return jsonify(status='success', data=dict(message=message))
def get_file(file_id, api=None): # TODO: remove this function and just use the Pillar SDK directly. if file_id is None: return None if api is None: api = system_util.pillar_api() try: return File.find(file_id, api=api) except ResourceNotFound: f = sys.exc_info()[2].tb_frame.f_back tb = traceback.format_stack(f=f, limit=2) log.warning('File %s not found, but requested from %s\n%s', file_id, request.url, ''.join(tb)) return None
def create(): """Endpoint hit by the automatic upload of a picture, currently used in the edit node form. Some sanity checks are already done in the fronted, but additional checks can be implemented here. """ name = request.form['name'] size = request.form['size'] content_type = request.form['type'] field_name = request.form['field_name'] project_id = request.form['project_id'] file_item = process_and_create_file(project_id, name, size, content_type) api = system_util.pillar_api() f = File.find(file_item['_id'], api=api) thumbnail_link = f.thumbnail('s', api=api) return jsonify(status='success', data=dict(id=file_item._id, link=thumbnail_link, field_name=field_name))
def __call__(self, field, **kwargs): html = super(CustomFileSelectWidget, self).__call__(field, **kwargs) file_format = self.file_format file_format_regex = '' if file_format and file_format == 'image': file_format_regex = '^image\/(gif|jpe?g|png|tif?f|tga)$' button = [u'<div class="form-upload-file">'] if field.data: api = system_util.pillar_api() try: # Load the existing file attached to the field file_item = File.find(field.data, api=api) except ResourceNotFound: pass else: filename = Markup.escape(file_item.filename) if file_item.content_type.split('/')[0] == 'image': # If a file of type image is available, display the preview button.append(u'<img class="preview-thumbnail" src="{0}" />'.format( file_item.thumbnail('s', api=api))) else: button.append(u'<p>{}</p>'.format(filename)) button.append(u'<ul class="form-upload-file-meta">') # File name button.append(u'<li class="name">{0}</li>'.format(filename)) # File size button.append(u'<li class="size">({0} MB)</li>'.format( round((file_item.length / 1024) * 0.001, 2))) # Image resolution (if image) button.append(u'<li class="dimensions">{0}x{1}</li>'.format( file_item.width, file_item.height)) # Delete button button.append(u'<li class="delete">' u'<a href="#" class="file_delete" ' u'data-field-name="{field_name}" ' u'data-file_id="{file_id}"> ' u'<i class="pi-trash"></i> Delete</a></li>'.format( field_name=field.name, file_id=field.data)) # Download button for original file button.append(u'<li class="original">' u'<a href="{}" class="file_original"> ' u'<i class="pi-download"></i>Original</a></li>' .format(file_item.link)) button.append(u'</ul>') upload_url = u'%s/storage/stream/{project_id}' % current_app.config[ 'PILLAR_SERVER_ENDPOINT'] button.append(u'<input class="fileupload" type="file" name="file" ' u'data-url="{url}" ' u'data-field-name="{name}" ' u'data-token="{token}" ' u'data-file-format="{file_format}">' u'<div class="form-upload-progress"> ' u'<div class="form-upload-progress-bar" role="progressbar" ' u'aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" ' u'style="width: 0%;"> ' u'</div> ' u'</div>'.format(url=upload_url, name=field.name, token=Markup.escape(current_user.id), file_format=Markup.escape(file_format_regex))) button.append(u'</div>') return HTMLString(html + u''.join(button))
def view(node_id): #import time #start = time.time() api = SystemUtility.attract_api() # Get node with embedded picture data try: node = Node.find(node_id + '/?embedded={"picture":1, "node_type":1}', api=api) except ResourceNotFound: return abort(404) except ForbiddenAccess: return abort(403) node_type_name = node.node_type.name # JsTree functionality. # This return a lightweight version of the node, to be used by JsTree in the # frontend. We have two possible cases: # - https://pillar/<node_id>/view?format=jstree (construct the whole expanded # tree starting from the node_id. Use only once) # - https://pillar/<node_id>/view?format=jstree&children=1 (deliver the # children of a node - use in the navigation of the tree) if request.args.get('format') and request.args.get('format') == 'jstree': if request.args.get('children') == '1': if node_type_name == 'storage': storage = StorageNode(node) # Check if we specify a path within the storage path = request.args.get('path') # Generate the storage listing listing = storage.browse(path) # Inject the current node id in the response, so that JsTree can # expose the storage_node property and use it for further queries listing['storage_node'] = node._id if 'children' in listing: for child in listing['children']: child['storage_node'] = node._id return jsonify(listing) else: return jsonify(jstree_build_children(node)) else: return jsonify(items=jstree_build_from_node(node)) # Continue to process the node (for HTML, HTML embeded and JSON responses) # Set the default name of the template path based on the node name template_path = os.path.join('nodes', 'custom', node_type_name) # Set the default action for a template. By default is view and we override # it only if we are working storage nodes, where an 'index' is also possible template_action = 'view' # XXX Code to detect a node of type asset, and aggregate file data if node_type_name == 'asset': node_file = File.find(node.properties.file, api=api) node_file_children = node_file.children(api=api) # Attach the file node to the asset node setattr(node, 'file', node_file) try: asset_type = node_file.content_type.split('/')[0] except AttributeError: asset_type = None if asset_type == 'video': # Process video type and select video template sources = [] if node_file_children: for f in node_file_children._items: sources.append(dict( type=f.content_type, src=f.link)) setattr(node, 'video_sources', json.dumps(sources)) setattr(node, 'file_children', node_file_children) template_path = os.path.join(template_path, asset_type) elif asset_type == 'image': # Process image type and select image template #setattr(node, 'file_children', node_file_children) template_path = os.path.join(template_path, asset_type) else: # Treat it as normal file (zip, blend, application, etc) template_path = os.path.join(template_path, 'file') # XXX The node is of type project elif node_type_name == 'project': if node.properties.picture_square: picture_square = File.find(node.properties.picture_square, api=api) node.properties.picture_square = picture_square if node.properties.picture_header: picture_header = File.find(node.properties.picture_header, api=api) node.properties.picture_header = picture_header if node.properties.nodes_latest: list_latest = [] for node_id in node.properties.nodes_latest: try: node_item = Node.find(node_id, { 'projection': '{"name":1, "user":1, "node_type":1}', 'embedded': '{"user":1}', }, api=api) list_latest.append(node_item) except ForbiddenAccess: list_latest.append(FakeNodeAsset()) node.properties.nodes_latest = list(reversed(list_latest)) if node.properties.nodes_featured: list_featured = [] for node_id in node.properties.nodes_featured: try: node_item = Node.find(node_id, { 'projection': '{"name":1, "user":1, "picture":1, "node_type":1}', 'embedded': '{"user":1}', }, api=api) picture = File.find(node_item.picture, api=api) node_item.picture = picture list_featured.append(node_item) except ForbiddenAccess: list_featured.append(FakeNodeAsset()) node.properties.nodes_featured = list(reversed(list_featured)) elif node_type_name == 'storage': storage = StorageNode(node) path = request.args.get('path') listing = storage.browse(path) node.name = listing['name'] listing['storage_node'] = node._id # If the item has children we are working with a group if 'children' in listing: for child in listing['children']: child['storage_node'] = node._id child['name'] = child['text'] child['content_type'] = os.path.dirname(child['type']) node.children = listing['children'] template_action = 'index' else: node.status = 'published' node.length = listing['size'] node.download_link = listing['signed_url'] # Get previews if node.picture: node.picture = File.find(node.picture._id, api=api) # Get Parent try: parent = Node.find(node['parent'], api=api) except KeyError: parent = None except ResourceNotFound: parent = None # Get children try: children = Node.all({ 'where': '{"parent": "%s"}' % node._id, 'embedded': '{"picture": 1, "node_type": 1}'}, api=api) children = children._items except ForbiddenAccess: return abort(403) for child in children: if child.picture: child.picture = File.find(child.picture._id, api=api) if request.args.get('format'): if request.args.get('format') == 'json': node = node.to_dict() node['url_edit'] = url_for('nodes.edit', node_id=node['_id']), if parent: parent = parent.to_dict() return_content = jsonify({ 'node': node, 'children': children.to_dict(), 'parent': parent }) else: embed_string = '' # Check if we want to embed the content via an AJAX call if request.args.get('embed'): if request.args.get('embed') == '1': # Define the prefix for the embedded template embed_string = '_embed' # Check if template exists on the filesystem template_path = '{0}/{1}{2}.html'.format(template_path, template_action, embed_string) template_path_full = os.path.join(app.config['TEMPLATES_PATH'], template_path) if not os.path.exists(template_path_full): return "Missing template" return_content = render_template(template_path, node=node, type_names=type_names(), parent=parent, children=children, config=app.config) #print(time.time() - start) return return_content
def shots_index(): max_results = 100 api = system_util.pillar_api() node_type_name = "shot" node_type_list = NodeType.all({ 'where': '{"name" : "%s"}' % node_type_name, }, api=api) node_type = node_type_list._items[0] nodes = Node.all({ 'where': '{"node_type" : "%s"}' % node_type._id, 'max_results': max_results, 'embedded': '{"picture":1}', 'sort' : "order"}, api=api) # Get the task node type object id node_type_list = NodeType.all({ 'where': '{"name" : "task"}', }, api=api) node_type_task = node_type_list._items[0] nodes_datatables = [] for node in nodes._items: tasks = Node.all({ 'where': '{"node_type" : "%s", "parent" : "%s"}'\ % (node_type_task._id, node._id), 'sort' : "order"}, api=api) shot_status = None try: shot_status = node.properties.status except: # Notify about missing status property. This should be prominent. pass data = { 'DT_RowId': "row_{0}".format(node._id), 'DT_RowAttr': {'data-shot-status':shot_status}, '_id': node._id, 'order': node.order, 'picture': None, 'name': node.name, #'description': node.description, 'notes': node.properties.notes, 'timing': { 'cut_in': node.properties.cut_in, 'cut_out': node.properties.cut_out }, 'url_view': url_for('nodes.view', node_id=node._id), 'url_edit': url_for('nodes.edit', node_id=node._id, embed=1), 'tasks': { 'animation': None, 'lighting': None, 'fx_hair': None, 'fx_grass': None, 'fx_smoke': None }, } if node.picture: # This is an address on the Attract server, so it should be built # entirely here data['picture'] = "{0}/file_server/file/{1}".format( app.config['PILLAR_SERVER_ENDPOINT'], node.picture.path) # Get previews picture_node = File.find(node.picture['_id'] + \ '/?embedded={"previews":1}', api=api) if picture_node.previews: for preview in picture_node.previews: if preview.size == 'm': data['picture_thumbnail'] = app.config['PILLAR_SERVER_ENDPOINT'] + "/file_server/file/" + preview.path break else: data['picture_thumbnail'] = data['picture'] if node.order is None: data['order'] = 0 for task in tasks._items: # If there are tasks assigned to the shot we loop through them and # match them with the existing data indexes. if task.name in data['tasks']: data['tasks'][task.name] = { 'name': task.name, 'status': task.properties.status, 'url_view': url_for('nodes.view', node_id=task._id, embed=1), 'url_edit': url_for('nodes.edit', node_id=task._id, embed=1), 'is_conflicting': task.properties.is_conflicting, 'is_processing': task.properties.is_rendering, 'is_open': task.properties.is_open } nodes_datatables.append(data) return jsonify(data=nodes_datatables)
def shots_index(): max_results = 100 api = system_util.pillar_api() node_type_name = "shot" node_type_list = NodeType.all( { 'where': '{"name" : "%s"}' % node_type_name, }, api=api) node_type = node_type_list._items[0] nodes = Node.all( { 'where': '{"node_type" : "%s"}' % node_type._id, 'max_results': max_results, 'embedded': '{"picture":1}', 'sort': "order" }, api=api) # Get the task node type object id node_type_list = NodeType.all({ 'where': '{"name" : "task"}', }, api=api) node_type_task = node_type_list._items[0] nodes_datatables = [] for node in nodes._items: tasks = Node.all({ 'where': '{"node_type" : "%s", "parent" : "%s"}'\ % (node_type_task._id, node._id), 'sort' : "order"}, api=api) shot_status = None try: shot_status = node.properties.status except: # Notify about missing status property. This should be prominent. pass data = { 'DT_RowId': "row_{0}".format(node._id), 'DT_RowAttr': { 'data-shot-status': shot_status }, '_id': node._id, 'order': node.order, 'picture': None, 'name': node.name, #'description': node.description, 'notes': node.properties.notes, 'timing': { 'cut_in': node.properties.cut_in, 'cut_out': node.properties.cut_out }, 'url_view': url_for('nodes.view', node_id=node._id), 'url_edit': url_for('nodes.edit', node_id=node._id, embed=1), 'tasks': { 'animation': None, 'lighting': None, 'fx_hair': None, 'fx_grass': None, 'fx_smoke': None }, } if node.picture: # This is an address on the Attract server, so it should be built # entirely here data['picture'] = "{0}/file_server/file/{1}".format( app.config['PILLAR_SERVER_ENDPOINT'], node.picture.path) # Get previews picture_node = File.find(node.picture['_id'] + \ '/?embedded={"previews":1}', api=api) if picture_node.previews: for preview in picture_node.previews: if preview.size == 'm': data['picture_thumbnail'] = app.config[ 'PILLAR_SERVER_ENDPOINT'] + "/file_server/file/" + preview.path break else: data['picture_thumbnail'] = data['picture'] if node.order is None: data['order'] = 0 for task in tasks._items: # If there are tasks assigned to the shot we loop through them and # match them with the existing data indexes. if task.name in data['tasks']: data['tasks'][task.name] = { 'name': task.name, 'status': task.properties.status, 'url_view': url_for('nodes.view', node_id=task._id, embed=1), 'url_edit': url_for('nodes.edit', node_id=task._id, embed=1), 'is_conflicting': task.properties.is_conflicting, 'is_processing': task.properties.is_rendering, 'is_open': task.properties.is_open } nodes_datatables.append(data) return jsonify(data=nodes_datatables)
def __call__(self, field, **kwargs): html = super(CustomFileSelectWidget, self).__call__(field, **kwargs) file_format = self.file_format file_format_regex = '' if file_format and file_format == 'image': file_format_regex = '^image\/(gif|jpe?g|png|tif?f|tga)$' button = ['<div class="form-upload-file">'] if field.data: api = system_util.pillar_api() try: # Load the existing file attached to the field file_item = File.find(field.data, api=api) except ResourceNotFound: pass else: button.append('<div class="form-upload-file-meta-container">') filename = Markup.escape(file_item.filename) if file_item.content_type.split('/')[0] == 'image': # If a file of type image is available, display the preview button.append( '<img class="preview-thumbnail" src="{0}" />'.format( file_item.thumbnail('s', api=api))) button.append('<ul class="form-upload-file-meta">') # File name button.append('<li class="name">{0}</li>'.format(filename)) # File size button.append('<li class="size">({0} MB)</li>'.format( round((file_item.length / 1024) * 0.001, 2))) # Image resolution (if image) if file_item.content_type.split('/')[0] == 'image': button.append('<li class="dimensions">{0}x{1}</li>'.format( file_item.width, file_item.height)) button.append('</ul>') button.append('<ul class="form-upload-file-actions">') # Download button for original file button.append( '<li class="original">' '<a href="{}" class="file_original"> ' '<i class="pi-download"></i>Original</a></li>'.format( file_item.link)) # Delete button button.append( '<li class="delete">' '<a href="#" class="file_delete" ' 'data-field-name="{field_name}" ' 'data-file_id="{file_id}"> ' '<i class="pi-trash"></i> Delete</a></li>'.format( field_name=field.name, file_id=field.data)) button.append('</ul>') button.append('</div>') upload_url = '%sstorage/stream/{project_id}' % current_app.config[ 'PILLAR_SERVER_ENDPOINT'] button.append( '<input class="fileupload" type="file" name="file" ' 'data-url="{url}" ' 'data-field-name="{name}" ' 'data-field-slug="{slug}" ' 'data-token="{token}" ' 'data-file-format="{file_format}">' '<div class="form-upload-progress"> ' '<div class="form-upload-progress-bar" role="progressbar" ' 'aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" ' 'style="width: 0%;"> ' '</div> ' '</div>'.format(url=upload_url, name=field.name, slug=field.name.replace('oid', 'slug'), token=Markup.escape(current_user.id), file_format=Markup.escape(file_format_regex))) button.append('</div>') return HTMLString(html + ''.join(button))
def view(node_id): #import time #start = time.time() api = SystemUtility.attract_api() # Get node with embedded picture data try: node = Node.find(node_id + '/?embedded={"picture":1, "node_type":1}', api=api) except ResourceNotFound: return abort(404) except ForbiddenAccess: return abort(403) node_type_name = node.node_type.name # JsTree functionality. # This return a lightweight version of the node, to be used by JsTree in the # frontend. We have two possible cases: # - https://pillar/<node_id>/view?format=jstree (construct the whole expanded # tree starting from the node_id. Use only once) # - https://pillar/<node_id>/view?format=jstree&children=1 (deliver the # children of a node - use in the navigation of the tree) if request.args.get('format') and request.args.get('format') == 'jstree': if request.args.get('children') == '1': if node_type_name == 'storage': storage = StorageNode(node) # Check if we specify a path within the storage path = request.args.get('path') # Generate the storage listing listing = storage.browse(path) # Inject the current node id in the response, so that JsTree can # expose the storage_node property and use it for further queries listing['storage_node'] = node._id if 'children' in listing: for child in listing['children']: child['storage_node'] = node._id return jsonify(listing) else: return jsonify(jstree_build_children(node)) else: return jsonify(items=jstree_build_from_node(node)) # Continue to process the node (for HTML, HTML embeded and JSON responses) # Set the default name of the template path based on the node name template_path = os.path.join('nodes', 'custom', node_type_name) # Set the default action for a template. By default is view and we override # it only if we are working storage nodes, where an 'index' is also possible template_action = 'view' # XXX Code to detect a node of type asset, and aggregate file data if node_type_name == 'asset': node_file = File.find(node.properties.file, api=api) node_file_children = node_file.children(api=api) # Attach the file node to the asset node setattr(node, 'file', node_file) try: asset_type = node_file.content_type.split('/')[0] except AttributeError: asset_type = None if asset_type == 'video': # Process video type and select video template sources = [] if node_file_children: for f in node_file_children._items: sources.append(dict(type=f.content_type, src=f.link)) setattr(node, 'video_sources', json.dumps(sources)) setattr(node, 'file_children', node_file_children) template_path = os.path.join(template_path, asset_type) elif asset_type == 'image': # Process image type and select image template #setattr(node, 'file_children', node_file_children) template_path = os.path.join(template_path, asset_type) else: # Treat it as normal file (zip, blend, application, etc) template_path = os.path.join(template_path, 'file') # XXX The node is of type project elif node_type_name == 'project': if node.properties.picture_square: picture_square = File.find(node.properties.picture_square, api=api) node.properties.picture_square = picture_square if node.properties.picture_header: picture_header = File.find(node.properties.picture_header, api=api) node.properties.picture_header = picture_header if node.properties.nodes_latest: list_latest = [] for node_id in node.properties.nodes_latest: try: node_item = Node.find(node_id, { 'projection': '{"name":1, "user":1, "node_type":1}', 'embedded': '{"user":1}', }, api=api) list_latest.append(node_item) except ForbiddenAccess: list_latest.append(FakeNodeAsset()) node.properties.nodes_latest = list(reversed(list_latest)) if node.properties.nodes_featured: list_featured = [] for node_id in node.properties.nodes_featured: try: node_item = Node.find(node_id, { 'projection': '{"name":1, "user":1, "picture":1, "node_type":1}', 'embedded': '{"user":1}', }, api=api) picture = File.find(node_item.picture, api=api) node_item.picture = picture list_featured.append(node_item) except ForbiddenAccess: list_featured.append(FakeNodeAsset()) node.properties.nodes_featured = list(reversed(list_featured)) elif node_type_name == 'storage': storage = StorageNode(node) path = request.args.get('path') listing = storage.browse(path) node.name = listing['name'] listing['storage_node'] = node._id # If the item has children we are working with a group if 'children' in listing: for child in listing['children']: child['storage_node'] = node._id child['name'] = child['text'] child['content_type'] = os.path.dirname(child['type']) node.children = listing['children'] template_action = 'index' else: node.status = 'published' node.length = listing['size'] node.download_link = listing['signed_url'] # Get previews if node.picture: node.picture = File.find(node.picture._id, api=api) # Get Parent try: parent = Node.find(node['parent'], api=api) except KeyError: parent = None except ResourceNotFound: parent = None # Get children try: children = Node.all( { 'where': '{"parent": "%s"}' % node._id, 'embedded': '{"picture": 1, "node_type": 1}' }, api=api) children = children._items except ForbiddenAccess: return abort(403) for child in children: if child.picture: child.picture = File.find(child.picture._id, api=api) if request.args.get('format'): if request.args.get('format') == 'json': node = node.to_dict() node['url_edit'] = url_for('nodes.edit', node_id=node['_id']), if parent: parent = parent.to_dict() return_content = jsonify({ 'node': node, 'children': children.to_dict(), 'parent': parent }) else: embed_string = '' # Check if we want to embed the content via an AJAX call if request.args.get('embed'): if request.args.get('embed') == '1': # Define the prefix for the embedded template embed_string = '_embed' # Check if template exists on the filesystem template_path = '{0}/{1}{2}.html'.format(template_path, template_action, embed_string) template_path_full = os.path.join(app.config['TEMPLATES_PATH'], template_path) if not os.path.exists(template_path_full): return "Missing template" return_content = render_template(template_path, node=node, type_names=type_names(), parent=parent, children=children, config=app.config) #print(time.time() - start) return return_content