def comments_index(): parent_id = request.args.get('parent_id') # Get data only if we format it api = system_util.pillar_api() if request.args.get('format'): nodes = Node.all({ 'where': '{"node_type" : "comment", "parent": "%s"}' % (parent_id), 'embedded': '{"user":1}'}, api=api) comments = [] for comment in nodes._items: # Query for first level children (comment replies) replies = Node.all({ 'where': '{"node_type" : "comment", "parent": "%s"}' % (comment._id), 'embedded': '{"user":1}'}, api=api) replies = replies._items if replies._items else None if replies: replies = [format_comment(reply, is_reply=True) for reply in replies] comments.append( format_comment(comment, is_reply=False, replies=replies)) if request.args.get('format') == 'json': return_content = jsonify(items=[c for c in comments if c is not None]) else: parent_node = Node.find(parent_id, api=api) project = Project.find(parent_node.project, api=api) has_method_POST = project.node_type_has_method('comment', 'POST', api=api) # Data will be requested via javascript return_content = render_template('nodes/custom/_comments.html', parent_id=parent_id, has_method_POST=has_method_POST) return return_content
def feeds_blogs(): """Global feed generator for latest blogposts across all projects""" feed = AtomFeed('Blender Cloud - Latest updates', feed_url=request.url, url=request.url_root) # Get latest blog posts api = system_util.pillar_api() latest_posts = Node.all({ 'where': {'node_type': 'post', 'properties.status': 'published'}, 'embedded': {'user': 1}, 'sort': '-_created', 'max_results': '15' }, api=api) # Populate the feed for post in latest_posts._items: author = post.user.fullname updated = post._updated if post._updated else post._created url = url_for_node(node=post) content = post.properties.content[:500] content = u'<p>{0}... <a href="{1}">Read more</a></p>'.format(content, url) feed.add(post.name, unicode(content), content_type='html', author=author, url=url, updated=updated, published=post._created) return feed.get_response()
def posts_view(project_id, url=None): """View individual blogpost""" api = SystemUtility.attract_api() node_type = NodeType.find_first({ 'where': '{"name" : "blog"}', 'projection': '{"name": 1}' }, api=api) blog = Node.find_one({ 'where': '{"node_type" : "%s", \ "parent": "%s"}' % (node_type._id, project_id), }, api=api) if url: try: post = Node.find_one({ 'where': '{"parent": "%s", "properties.url": "%s"}' % (blog._id, url), 'embedded': '{"picture":1, "node_type": 1}', }, api=api) except ResourceNotFound: return abort(404) return render_template( 'nodes/custom/post/view.html', blog=blog, node=post) else: # Render all posts posts = Node.all({ 'where': '{"parent": "%s"}' % (blog._id), 'embedded': '{"picture":1}', 'sort': '-_created' }, api=api) return render_template( 'nodes/custom/blog/index.html', blog=blog, posts=posts._items)
def projects(self): """Get list of project for the user""" # Find node_type project id (this could become static) node_type = NodeType.find_first({ 'where': '{"name" : "project"}', }, api=self.api) if not node_type: return abort(404) # Define key for querying for the project if self.is_organization: user_path = 'properties.organization' else: user_path = 'user' # Query for the project # TODO currently, this system is weak since we rely on the user property # of a node when searching for a project using the user it. This allows # us to find a project that belongs to an organization also by requesting # the user that originally created the node. This can be fixed by # introducing a 'user' property in the project node type. projects = Node.all({ 'where': '{"node_type" : "%s", "%s": "%s"}'\ % (node_type._id, user_path, self._id), }, api=self.api) for project in projects._items: attach_project_pictures(project, self.api) return projects
def homepage(): """Homepage""" if not current_user.is_authenticated(): return render_template( 'join.html', title="join") # Get latest blog posts api = system_util.pillar_api() latest_posts = Node.all({ 'projection': {'name': 1, 'project': 1, 'user': 1, 'node_type': 1, 'picture': 1, 'properties.status': 1, 'properties.url': 1}, 'where': {'node_type': 'post', 'properties.status': 'published'}, 'embedded': {'user': 1, 'project': 1}, 'sort': '-_created', 'max_results': '3' }, api=api) # Append picture Files to last_posts for post in latest_posts._items: if post.picture: post.picture = get_file(post.picture, api=api) # Get latest assets added to any project latest_assets = Node.latest('assets', api=api) # Append picture Files to latest_assets for asset in latest_assets._items: if asset.picture: asset.picture = get_file(asset.picture, api=api) # Get latest comments to any node latest_comments = Node.latest('comments', api=api) # Parse results for replies for comment in latest_comments._items: if comment.properties.is_reply: comment.parent = Node.find(comment.parent.parent, api=api) else: comment.parent = comment.parent main_project = Project.find(app.config['MAIN_PROJECT_ID'], api=api) main_project.picture_square = get_file(main_project.picture_square, api=api) main_project.picture_header = get_file(main_project.picture_header, api=api) return render_template( 'homepage.html', main_project=main_project, latest_posts=latest_posts._items, latest_assets=latest_assets._items, latest_comments=latest_comments._items, api=api)
def get_node_children(node_id, node_type_name, user_id): """This function is currently unused since it does not give significant performance improvements. """ api = system_util.pillar_api() if node_type_name == 'group': published_status = ',"properties.status": "published"' else: published_status = '' children = Node.all({ 'where': '{"parent": "%s" %s}' % (node_id, published_status), 'embedded': '{"node_type": 1}'}, api=api) return children.to_dict()
def comments_index(): parent_id = request.args.get('parent_id') if request.args.get('format'): # Get data only if we format it api = SystemUtility.attract_api() node_type = NodeType.find_first({ 'where': '{"name" : "comment"}', }, api=api) nodes = Node.all({ 'where': '{"node_type" : "%s", "parent": "%s"}' % (node_type._id, parent_id), 'embedded': '{"user":1}'}, api=api) comments = [] for comment in nodes._items: # Query for first level children (comment replies) replies = Node.all({ 'where': '{"node_type" : "%s", "parent": "%s"}' % (node_type._id, comment._id), 'embedded': '{"user":1}'}, api=api) replies = replies._items if replies._items else None if replies: replies = [format_comment(reply, is_reply=True) for reply in replies] comments.append( format_comment(comment, is_reply=False, replies=replies)) if request.args.get('format') == 'json': return_content = jsonify(items=comments) else: # Data will be requested via javascript return_content = render_template('nodes/custom/_comments.html', parent_id=parent_id) return return_content
def index(): """Custom production stats entry point """ api = SystemUtility.attract_api() node_type_list = NodeType.all( {'where': '{"name": "%s"}' % ('shot')}, api=api) node_type = node_type_list['_items'][0] nodes = Node.all({ 'where': '{"node_type": "%s"}' % (node_type['_id']), 'max_results': 100, 'sort' : "order"}, api=api) node_statuses = {} node_totals = { 'count': 0, 'frames': 0 } for shot in nodes._items: status = shot.properties.status if status not in node_statuses: # If they key does not exist, initialize with defaults node_statuses[status] = { 'count': 0, 'frames': 0} # Calculate shot duration and increase status count frames = shot.properties.cut_out - shot.properties.cut_in node_statuses[status]['count'] += 1 node_statuses[status]['frames'] += frames # Update the global stats node_totals['count'] += 1 node_totals['frames'] += frames for node_status in node_statuses: # Calculate completion percentage based on total duration print node_statuses[node_status]['frames'] print node_totals['frames'] node_statuses[node_status]['completion'] = percentage( node_statuses[node_status]['frames'], node_totals['frames']) return render_template( 'stats/index.html', node_statuses=node_statuses)
def get_projects(category): """Utility to get projects based on category. Should be moved on the API and improved with more extensive filtering capabilities. """ api = SystemUtility.attract_api() node_type = NodeType.find_first({ 'where': '{"name" : "project"}', 'projection': '{"name": 1}' }, api=api) projects = Node.all({ 'where': '{"node_type" : "%s", \ "properties.category": "%s"}' % (node_type._id, category), 'embedded': '{"picture":1}', }, api=api) for project in projects._items: attach_project_pictures(project, api) return projects
def index(node_type_name=""): """Generic function to list all nodes """ # Pagination index page = request.args.get('page', 1) max_results = 50 api = SystemUtility.attract_api() if node_type_name == "": node_type_name = "shot" node_type = NodeType.find_first({ 'where': '{"name" : "%s"}' % node_type_name, }, api=api) if not node_type: return "Empty NodeType list", 200 nodes = Node.all({ 'where': '{"node_type" : "%s"}' % (node_type._id), 'max_results': max_results, 'page': page, 'embedded': '{"picture":1}', 'sort' : "order"}, api=api) # Build the pagination object pagination = Pagination(int(page), max_results, nodes._meta.total) template = '{0}/index.html'.format(node_type_name) try: return render_template( template, title=node_type_name, nodes=nodes, node_type=node_type, type_names=type_names(), pagination=pagination) except TemplateNotFound: return render_template( 'nodes/index.html', title=node_type_name, nodes=nodes, node_type=node_type, type_names=type_names(), pagination=pagination)
def posts_view(project_id, url=None): """View individual blogpost""" api = SystemUtility.attract_api() node_type = NodeType.find_first( { 'where': '{"name" : "blog"}', 'projection': '{"name": 1}' }, api=api) blog = Node.find_one( { 'where': '{"node_type" : "%s", \ "parent": "%s"}' % (node_type._id, project_id), }, api=api) if url: try: post = Node.find_one( { 'where': '{"parent": "%s", "properties.url": "%s"}' % (blog._id, url), 'embedded': '{"picture":1, "node_type": 1}', }, api=api) except ResourceNotFound: return abort(404) return render_template('nodes/custom/post/view.html', blog=blog, node=post) else: # Render all posts posts = Node.all( { 'where': '{"parent": "%s"}' % (blog._id), 'embedded': '{"picture":1}', 'sort': '-_created' }, api=api) return render_template('nodes/custom/blog/index.html', blog=blog, posts=posts._items)
def shared_image_nodes(home_project_id, api): """Returns a list of pillarsdk.Node objects.""" parent_group = Node.find_first( { 'where': { 'project': home_project_id, 'node_type': 'group', 'parent': None, 'name': IMAGE_SHARING_GROUP_NODE_NAME }, 'projection': { '_id': 1 } }, api=api) if not parent_group: log.debug('No image sharing parent node found.') return [] nodes = Node.all( { 'where': { 'project': home_project_id, 'node_type': 'asset', 'properties.content_type': 'image', 'parent': parent_group['_id'] }, 'sort': '-_created', 'projection': { '_created': 1, 'name': 1, 'picture': 1, 'short_code': 1, } }, api=api) nodes = nodes._items or [] for node in nodes: node.picture = utils.get_file(node.picture) return nodes
def jstree_get_children(node_id): api = SystemUtility.attract_api() children_list = [] if node_id.startswith('n_'): node_id = node_id.split('_')[1] try: children = Node.all({ 'projection': '{"name": 1, "parent": 1, "node_type": 1, "properties": 1}', 'embedded': '{"node_type": 1}', 'where': '{"parent": "%s"}' % node_id}, api=api) for child in children._items: # Skip nodes of type comment if child.node_type.name != 'comment': children_list.append(jstree_parse_node(child)) except ForbiddenAccess: pass return children_list
def render_page(): # Get latest posts api = system_util.pillar_api() project = Project.find_first({'where': {'url': community_url}}, api=api) if not project: abort(404) feed = AtomFeed(project.name + ' - ' + _('Latest updates'), feed_url=request.url, url=request.url_root) latest_posts = Node.all({ 'where': {'node_type': 'dillo_post', 'properties.status': 'published', 'project': project['_id']}, 'embedded': {'user': 1}, 'sort': '-_created', 'max_results': '15' }, api=api) populate_feed(feed, latest_posts) return feed.get_response()
def jstree_get_children(node_id, project_id=None): api = system_util.pillar_api() children_list = [] lookup = { 'projection': { 'name': 1, 'parent': 1, 'node_type': 1, 'properties.order': 1, 'properties.status': 1, 'properties.content_type': 1, 'user': 1, 'project': 1 }, 'sort': [('properties.order', 1), ('_created', 1)] } if node_id: if node_id.startswith('n_'): node_id = node_id.split('_')[1] lookup['where'] = {'parent': node_id} elif project_id: lookup['where'] = {'project': project_id, 'parent': {'$exists': False}} try: children = Node.all(lookup, api=api) for child in children['_items']: # Skip nodes of type comment if child.node_type not in ['comment', 'post']: if child.properties.status == 'published': children_list.append(jstree_parse_node(child)) elif child.node_type == 'blog': children_list.append(jstree_parse_node(child)) elif current_user.is_authenticated( ) and child.user == current_user.objectid: children_list.append(jstree_parse_node(child)) except ForbiddenAccess: pass return children_list
def render_page(): feed = AtomFeed('Blender Cloud - Latest updates', feed_url=ensure_schema(request.url), url=ensure_schema(request.url_root)) # Get latest blog posts api = system_util.pillar_api() latest_posts = Node.all({ 'where': {'node_type': 'post', 'properties.status': 'published'}, 'embedded': {'user': 1}, 'sort': '-_created', 'max_results': '15' }, api=api) newest = None # Populate the feed for post in latest_posts._items: author = post.user.fullname or post.user.username updated = post._updated if post._updated else post._created url = ensure_schema(urllib.parse.urljoin(request.host_url, url_for_node(node=post))) content = post.properties.content[:500] content = '<p>{0}... <a href="{1}">Read more</a></p>'.format(content, url) if newest is None: newest = updated else: newest = max(newest, updated) feed.add(post.name, str(content), content_type='html', author=author, url=url, updated=updated, published=post._created) resp = feed.get_response() if newest is not None: resp.headers['Last-Modified'] = newest.strftime(current_app.config['RFC1123_DATE_FORMAT']) return resp
def jstree_get_children(node_id): api = SystemUtility.attract_api() children_list = [] if node_id.startswith('n_'): node_id = node_id.split('_')[1] try: children = Node.all( { 'projection': '{"name": 1, "parent": 1, "node_type": 1, "properties": 1}', 'embedded': '{"node_type": 1}', 'where': '{"parent": "%s"}' % node_id }, api=api) for child in children._items: # Skip nodes of type comment if child.node_type.name != 'comment': children_list.append(jstree_parse_node(child)) except ForbiddenAccess: pass return children_list
def get_projects(category): """Utility to get projects based on category. Should be moved on the API and improved with more extensive filtering capabilities. """ api = SystemUtility.attract_api() node_type = NodeType.find_first( { 'where': '{"name" : "project"}', 'projection': '{"name": 1}' }, api=api) projects = Node.all( { 'where': '{"node_type" : "%s", \ "properties.category": "%s"}' % (node_type._id, category), 'embedded': '{"picture":1}', }, api=api) for project in projects._items: attach_project_pictures(project, api) return projects
def feeds_blogs(): """Global feed generator for latest blogposts across all projects""" feed = AtomFeed('Blender Cloud - Latest updates', feed_url=request.url, url=request.url_root) # Get latest blog posts api = system_util.pillar_api() latest_posts = Node.all( { 'where': { 'node_type': 'post', 'properties.status': 'published' }, 'embedded': { 'user': 1 }, 'sort': '-_created', 'max_results': '15' }, api=api) # Populate the feed for post in latest_posts._items: author = post.user.fullname updated = post._updated if post._updated else post._created url = url_for_node(node=post) content = post.properties.content[:500] content = u'<p>{0}... <a href="{1}">Read more</a></p>'.format( content, url) feed.add(post.name, unicode(content), content_type='html', author=author, url=url, updated=updated, published=post._created) return feed.get_response()
def synced_blender_versions(home_project_id, api): """Returns a list of Blender versions with synced settings. Returns a list of {'version': '2.77', 'date': datetime.datetime()} dicts. Returns an empty list if no Blender versions were synced. """ sync_group = Node.find_first({ 'where': {'project': home_project_id, 'node_type': 'group', 'parent': None, 'name': SYNC_GROUP_NODE_NAME}, 'projection': {'_id': 1}}, api=api) if not sync_group: return [] sync_nodes = Node.all( { 'where': {'project': home_project_id, 'node_type': 'group', 'parent': sync_group['_id']}, 'projection': { 'name': 1, '_updated': 1, }, 'sort': [('_updated', -1)], }, api=api) sync_nodes = sync_nodes._items if not sync_nodes: return [] return [{'version': node.name, 'date': node._updated} for node in sync_nodes]
def view(node_id, extra_template_args: dict = None): api = system_util.pillar_api() # Get node, we'll embed linked objects later. try: node = Node.find(node_id, api=api) except ResourceNotFound: return render_template('errors/404_embed.html') except ForbiddenAccess: return render_template('errors/403_embed.html') node_type_name = node.node_type if node_type_name == 'post' and not request.args.get('embed'): # Posts shouldn't be shown at this route (unless viewed embedded, tipically # after an edit. Redirect to the correct one. return redirect(url_for_node(node=node)) # 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' def allow_link(): """Helper function to cross check if the user is authenticated, and it is has the 'subscriber' role. Also, we check if the node has world GET permissions, which means it's free. """ # Check if node permissions for the world exist (if node is free) if node.permissions and node.permissions.world: return 'GET' in node.permissions.world return current_user.has_cap('subscriber') link_allowed = allow_link() node_type_handlers = { 'asset': _view_handler_asset, 'storage': _view_handler_storage, 'texture': _view_handler_texture, 'hdri': _view_handler_hdri, } if node_type_name in node_type_handlers: handler = node_type_handlers[node_type_name] template_path, template_action = handler(node, template_path, template_action, link_allowed) # Fetch linked resources. node.picture = get_file(node.picture, api=api) node.user = node.user and pillarsdk.User.find(node.user, api=api) try: node.parent = node.parent and pillarsdk.Node.find(node.parent, api=api) except ForbiddenAccess: # This can happen when a node has world-GET, but the parent doesn't. node.parent = None # Get children children_projection = { 'project': 1, 'name': 1, 'picture': 1, 'parent': 1, 'node_type': 1, 'properties.order': 1, 'properties.status': 1, 'user': 1, 'properties.content_type': 1 } children_where = {'parent': node._id} if node_type_name in GROUP_NODES: children_where['properties.status'] = 'published' children_projection['permissions.world'] = 1 else: children_projection['properties.files'] = 1 children_projection['properties.is_tileable'] = 1 try: children = Node.all( { 'projection': children_projection, 'where': children_where, 'sort': [('properties.order', 1), ('name', 1)] }, api=api) except ForbiddenAccess: return render_template('errors/403_embed.html') children = children._items for child in children: child.picture = get_file(child.picture, api=api) # Overwrite the file length by the biggest variation, if any. if node.file and node.file_variations: node.file.length = max(var.length for var in node.file_variations) if request.args.get('format') == 'json': node = node.to_dict() node['url_edit'] = url_for('nodes.edit', node_id=node['_id']) return jsonify({ 'node': node, 'children': children.to_dict() if children else {}, 'parent': node['parent'] if 'parent' in node else {} }) if 't' in request.args: template_path = os.path.join('nodes', 'custom', 'asset') template_action = 'view_theatre' template_path = '{0}/{1}_embed.html'.format(template_path, template_action) # Full override for AMP view if request.args.get('format') == 'amp': template_path = 'nodes/view_amp.html' write_access = 'PUT' in (node.allowed_methods or set()) if extra_template_args is None: extra_template_args = {} try: return render_template(template_path, node_id=node._id, node=node, parent=node.parent, children=children, config=current_app.config, write_access=write_access, api=api, **extra_template_args) except TemplateNotFound: log.error('Template %s does not exist for node type %s', template_path, node_type_name) return render_template('nodes/error_type_not_found.html', node_id=node._id, node=node, parent=node.parent, children=children, config=current_app.config, write_access=write_access, api=api, **extra_template_args)
def task_edit(): """We want to be able to edit the following properties: - status - owners - description - picture (optional) """ api = system_util.pillar_api() task_id = request.form['task_id'] task = Node.find(task_id, api=api) task.description = request.form['task_description'] if request.form['task_revision']: task.properties.revision = int(request.form['task_revision']) task.properties.status = request.form['task_status'] task.properties.filepath = request.form['task_filepath'] task.properties.owners.users = request.form.getlist('task_owners_users[]') siblings = Node.all({ 'where': 'parent==ObjectId("%s")' % task.parent, 'embedded': '{"picture":1, "user":1}'}, api=api) def check_conflict(task_current, task_sibling): return revsion_conflict[task_current.name](task_current, task_sibling) def task_animation(task_current, task_sibling): if task_sibling.name in ['fx_hair', 'fx_smoke', 'fx_grass', 'lighting']: if task_current.properties.revision > task_sibling.properties.revision: return True return False def task_lighting(task_current, task_sibling): if task_sibling.name in ['fx_hair', 'fx_smoke', 'fx_grass', 'animation']: if task_current.properties.revision < task_sibling.properties.revision: return True return False def task_fx_hair(task_current, task_sibling): if task_sibling.name in ['animation']: if task_current.properties.revision < task_sibling.properties.revision: return True if task_sibling.name in ['lighting']: if task_current.properties.revision > task_sibling.properties.revision: return True return False def task_fx_grass(task_current, task_sibling): pass def task_fx_smoke(task_current, task_sibling): pass revsion_conflict = { 'animation': task_animation, 'lighting': task_lighting, 'fx_hair': task_fx_hair, 'fx_grass': task_fx_grass, 'fx_smoke': task_fx_smoke } if task.properties.revision: for sibling in siblings._items: if sibling.properties.revision and sibling._id != task_id: if check_conflict(task, sibling) == True: task.properties.is_conflicting = True break else: task.properties.is_conflicting = False task.update(api=api) return jsonify(task.to_dict())
def posts_view(project_id, url=None): """View individual blogpost""" api = system_util.pillar_api() # Fetch project (for backgroud images and links generation) project = Project.find(project_id, api=api) attach_project_pictures(project, api) try: blog = Node.find_one( { 'where': { 'node_type': 'blog', 'project': project_id }, }, api=api) except ResourceNotFound: abort(404) if url: try: post = Node.find_one( { 'where': '{"parent": "%s", "properties.url": "%s"}' % (blog._id, url), 'embedded': '{"node_type": 1, "user": 1}', }, api=api) if post.picture: post.picture = get_file(post.picture, api=api) except ResourceNotFound: return abort(404) # If post is not published, check that the user is also the author of # the post. If not, return 404. if post.properties.status != "published": if current_user.is_authenticated(): if not post.has_method('PUT'): abort(403) else: abort(403) return render_template('nodes/custom/post/view.html', blog=blog, node=post, project=project, title='blog', api=api) else: node_type_post = project.get_node_type('post') status_query = "" if blog.has_method( 'PUT') else ', "properties.status": "published"' posts = Node.all( { 'where': '{"parent": "%s" %s}' % (blog._id, status_query), 'embedded': '{"user": 1}', 'sort': '-_created' }, api=api) for post in posts._items: post.picture = get_file(post.picture, api=api) return render_template('nodes/custom/blog/index.html', node_type_post=node_type_post, posts=posts._items, project=project, title='blog', api=api)
def project_landing(project_url): """Override Pillar project_view endpoint completely. The first part of the function is identical to the one in Pillar, but the second part (starting with 'Load custom project properties') extends the behaviour to support film project landing pages. """ template_name = None if request.args.get('format') == 'jstree': log.warning( 'projects.view(%r) endpoint called with format=jstree, ' 'redirecting to proper endpoint. URL is %s; referrer is %s', project_url, request.url, request.referrer) return redirect(url_for('projects.jstree', project_url=project_url)) api = system_util.pillar_api() project = find_project_or_404(project_url, embedded={'header_node': 1}, api=api) # Load the header video file, if there is any. header_video_file = None header_video_node = None if project.header_node and project.header_node.node_type == 'asset' and \ project.header_node.properties.content_type == 'video': header_video_node = project.header_node header_video_file = get_file(project.header_node.properties.file) header_video_node.picture = get_file(header_video_node.picture) extra_context = { 'header_video_file': header_video_file, 'header_video_node': header_video_node } # Load custom project properties. If the project has a 'cloud' extension prop, # render it using the projects/landing.html template and try to attach a # number of additional attributes (pages, images, etc.). if 'extension_props' in project and EXTENSION_NAME in project[ 'extension_props']: extension_props = project['extension_props'][EXTENSION_NAME] extension_props['logo'] = get_file(extension_props['logo']) pages = Node.all( { 'where': { 'project': project._id, 'node_type': 'page', '_deleted': { '$ne': True } }, 'projection': { 'name': 1 } }, api=api) extra_context.update({'pages': pages._items}) template_name = 'projects/landing.html' return render_project(project, api, extra_context=extra_context, template_name=template_name)
def view(node_id): api = system_util.pillar_api() # Get node, we'll embed linked objects later. try: node = Node.find(node_id, api=api) except ResourceNotFound: return render_template('errors/404_embed.html') except ForbiddenAccess: return render_template('errors/403_embed.html') node_type_name = node.node_type if node_type_name == 'post': # Posts shouldn't be shown using this end point, redirect to the correct one. return redirect(url_for_node(node=node)) # 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' def allow_link(): """Helper function to cross check if the user is authenticated, and it is has the 'subscriber' role. Also, we check if the node has world GET permissions, which means it's free. """ # Check if node permissions for the world exist (if node is free) if node.permissions and node.permissions.world: return 'GET' in node.permissions.world if current_user.is_authenticated(): allowed_roles = {u'subscriber', u'demo', u'admin'} return bool(allowed_roles.intersection(current_user.roles or ())) return False link_allowed = allow_link() node_type_handlers = { 'asset': _view_handler_asset, 'storage': _view_handler_storage, 'texture': _view_handler_texture, } if node_type_name in node_type_handlers: handler = node_type_handlers[node_type_name] template_path, template_action = handler(node, template_path, template_action, link_allowed) # Fetch linked resources. node.picture = get_file(node.picture, api=api) node.user = node.user and pillarsdk.User.find(node.user, api=api) node.parent = node.parent and pillarsdk.Node.find(node.parent, api=api) # Get children children_projection = { 'project': 1, 'name': 1, 'picture': 1, 'parent': 1, 'node_type': 1, 'properties.order': 1, 'properties.status': 1, 'user': 1, 'properties.content_type': 1 } children_where = {'parent': node._id} if node_type_name == 'group': children_where['properties.status'] = 'published' children_projection['permissions.world'] = 1 else: children_projection['properties.files'] = 1 children_projection['properties.is_tileable'] = 1 try: children = Node.all( { 'projection': children_projection, 'where': children_where, 'sort': [('properties.order', 1), ('name', 1)] }, api=api) except ForbiddenAccess: return render_template('errors/403_embed.html') children = children._items for child in children: child.picture = get_file(child.picture, api=api) if request.args.get('format') == 'json': node = node.to_dict() node['url_edit'] = url_for('nodes.edit', node_id=node['_id']), return jsonify({ 'node': node, 'children': children.to_dict(), 'parent': node.parent.to_dict() if node.parent else {} }) # Check if template exists on the filesystem template_path = '{0}/{1}_embed.html'.format(template_path, template_action) template_path_full = os.path.join(app.config['TEMPLATES_PATH'], template_path) if not os.path.exists(template_path_full): raise NotFound("Missing template '{0}'".format(template_path)) return render_template(template_path, node_id=node._id, node=node, parent=node.parent, children=children, config=app.config, api=api)
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 project_navigation_links(project: typing.Type[Project], api) -> list: """Returns a list of nodes for the project, for top navigation display. Args: project: A Project object. api: the api client credential. Returns: A list of links for the Project. For example we display a link to the project blog if present, as well as pages. The list is structured as follows: [{'url': '/p/spring/about', 'label': 'About'}, {'url': '/p/spring/blog', 'label': 'Blog'}] """ links = [] # Fetch the blog blog = Node.find_first( { 'where': { 'project': project._id, 'node_type': 'blog', '_deleted': { '$ne': True } }, 'projection': { 'name': 1, } }, api=api) if blog: links.append({ 'url': finders.find_url_for_node(blog), 'label': blog.name, 'slug': 'blog' }) # Fetch pages pages = Node.all( { 'where': { 'project': project._id, 'node_type': 'page', '_deleted': { '$ne': True } }, 'projection': { 'name': 1, 'properties.url': 1 } }, api=api) # Process the results and append the links to the list for p in pages._items: links.append({ 'url': finders.find_url_for_node(p), 'label': p.name, 'slug': p.properties.url }) return links
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 homepage(): """Homepage""" if not current_user.is_authenticated(): return render_template('join.html', title="join") # Get latest blog posts api = system_util.pillar_api() latest_posts = Node.all( { 'projection': { 'name': 1, 'project': 1, 'user': 1, 'node_type': 1, 'picture': 1, 'properties.status': 1, 'properties.url': 1 }, 'where': { 'node_type': 'post', 'properties.status': 'published' }, 'embedded': { 'user': 1, 'project': 1 }, 'sort': '-_created', 'max_results': '3' }, api=api) # Append picture Files to last_posts for post in latest_posts._items: if post.picture: post.picture = get_file(post.picture, api=api) # Get latest assets added to any project latest_assets = Node.latest('assets', api=api) # Append picture Files to latest_assets for asset in latest_assets._items: if asset.picture: asset.picture = get_file(asset.picture, api=api) # Get latest comments to any node latest_comments = Node.latest('comments', api=api) # Parse results for replies for comment in latest_comments._items: if comment.properties.is_reply: comment.parent = Node.find(comment.parent.parent, api=api) else: comment.parent = comment.parent main_project = Project.find(app.config['MAIN_PROJECT_ID'], api=api) main_project.picture_square = get_file(main_project.picture_square, api=api) main_project.picture_header = get_file(main_project.picture_header, api=api) return render_template('homepage.html', main_project=main_project, latest_posts=latest_posts._items, latest_assets=latest_assets._items, latest_comments=latest_comments._items, api=api)
def view_node(project_url, node_id): """Entry point to view a node in the context of a project""" # Some browsers mangle URLs and URL-encode /p/{p-url}/#node-id if node_id.startswith('#'): return redirect(url_for('projects.view_node', project_url=project_url, node_id=node_id[1:]), code=301) # permanent redirect theatre_mode = 't' in request.args api = system_util.pillar_api() # First we check if it's a simple string, in which case we are looking for # a static page. Maybe we could use bson.objectid.ObjectId.is_valid(node_id) if not utils.is_valid_id(node_id): # raise wz_exceptions.NotFound('No such node') project, node = render_node_page(project_url, node_id, api) else: # Fetch the node before the project. If this user has access to the # node, we should be able to get the project URL too. try: node = Node.find(node_id, api=api) except ForbiddenAccess: return render_template('errors/403.html'), 403 except ResourceNotFound: raise wz_exceptions.NotFound('No such node') try: project = Project.find_one({'where': {"url": project_url, '_id': node.project}}, api=api) except ResourceNotFound: # In theatre mode, we don't need access to the project at all. if theatre_mode: project = None else: raise wz_exceptions.NotFound('No such project') og_picture = node.picture = utils.get_file(node.picture, api=api) if project: if not node.picture: og_picture = utils.get_file(project.picture_header, api=api) project.picture_square = utils.get_file(project.picture_square, api=api) # Append _theatre to load the proper template theatre = '_theatre' if theatre_mode else '' if node.node_type == 'page': pages = Node.all({ 'where': {'project': project._id, 'node_type': 'page'}, 'projection': {'name': 1}}, api=api) return render_template('nodes/custom/page/view_embed.html', api=api, node=node, project=project, pages=pages._items, og_picture=og_picture,) extension_sidebar_links = current_app.extension_sidebar_links(project) return render_template('projects/view{}.html'.format(theatre), api=api, project=project, node=node, show_node=True, show_project=False, og_picture=og_picture, extension_sidebar_links=extension_sidebar_links)
def posts_view(project_id=None, project_url=None, url=None): """View individual blogpost""" if bool(project_id) == bool(project_url): raise ValueError('posts_view(): pass either project_id or project_url') api = system_util.pillar_api() # Fetch project (for backgroud images and links generation) if project_id: project = Project.find(project_id, api=api) else: project = Project.find_one({'where': {'url': project_url}}, api=api) project_id = project['_id'] attach_project_pictures(project, api) blog = Node.find_one( { 'where': { 'node_type': 'blog', 'project': project_id }, }, api=api) status_query = "" if blog.has_method( 'PUT') else ', "properties.status": "published"' posts = Node.all( { 'where': '{"parent": "%s" %s}' % (blog._id, status_query), 'embedded': '{"user": 1}', 'sort': '-_created' }, api=api) for post in posts._items: post.picture = get_file(post.picture, api=api) post['properties'][ 'content'] = pillar.web.nodes.attachments.render_attachments( post, post['properties']['content']) # Use the *_main_project.html template for the main blog main_project_template = '_main_project' if project_id == current_app.config[ 'MAIN_PROJECT_ID'] else '' if url: post = Node.find_one( { 'where': { 'parent': blog._id, 'properties.url': url }, 'embedded': { 'node_type': 1, 'user': 1 }, }, api=api) if post.picture: post.picture = get_file(post.picture, api=api) # If post is not published, check that the user is also the author of # the post. If not, return 404. if post.properties.status != "published": if not (current_user.is_authenticated and post.has_method('PUT')): abort(403) post['properties'][ 'content'] = pillar.web.nodes.attachments.render_attachments( post, post['properties']['content']) return render_template( 'nodes/custom/post/view{0}.html'.format(main_project_template), blog=blog, node=post, posts=posts._items, project=project, title='blog', api=api) else: node_type_post = project.get_node_type('post') template_path = 'nodes/custom/blog/index.html' return render_template( 'nodes/custom/blog/index{0}.html'.format(main_project_template), node_type_post=node_type_post, posts=posts._items, project=project, title='blog', api=api)
def task_edit(): """We want to be able to edit the following properties: - status - owners - description - picture (optional) """ api = SystemUtility.attract_api() task_id = request.form["task_id"] task = Node.find(task_id, api=api) task.description = request.form["task_description"] if request.form["task_revision"]: task.properties.revision = int(request.form["task_revision"]) task.properties.status = request.form["task_status"] task.properties.filepath = request.form["task_filepath"] task.properties.owners.users = request.form.getlist("task_owners_users[]") siblings = Node.all( {"where": 'parent==ObjectId("%s")' % task.parent, "embedded": '{"picture":1, "user":1}'}, api=api ) def check_conflict(task_current, task_sibling): return revsion_conflict[task_current.name](task_current, task_sibling) def task_animation(task_current, task_sibling): if task_sibling.name in ["fx_hair", "fx_smoke", "fx_grass", "lighting"]: if task_current.properties.revision > task_sibling.properties.revision: return True return False def task_lighting(task_current, task_sibling): if task_sibling.name in ["fx_hair", "fx_smoke", "fx_grass", "animation"]: if task_current.properties.revision < task_sibling.properties.revision: return True return False def task_fx_hair(task_current, task_sibling): if task_sibling.name in ["animation"]: if task_current.properties.revision < task_sibling.properties.revision: return True if task_sibling.name in ["lighting"]: if task_current.properties.revision > task_sibling.properties.revision: return True return False def task_fx_grass(task_current, task_sibling): pass def task_fx_smoke(task_current, task_sibling): pass revsion_conflict = { "animation": task_animation, "lighting": task_lighting, "fx_hair": task_fx_hair, "fx_grass": task_fx_grass, "fx_smoke": task_fx_smoke, } if task.properties.revision: for sibling in siblings._items: if sibling.properties.revision and sibling._id != task_id: if check_conflict(task, sibling) == True: task.properties.is_conflicting = True break else: task.properties.is_conflicting = False task.update(api=api) return jsonify(task.to_dict())
def view(node_id): api = system_util.pillar_api() # Get node, we'll embed linked objects later. try: node = Node.find(node_id, api=api) except ResourceNotFound: return render_template('errors/404_embed.html') except ForbiddenAccess: return render_template('errors/403_embed.html') node_type_name = node.node_type if node_type_name == 'post': # Posts shouldn't be shown using this end point, redirect to the correct one. return redirect(url_for_node(node=node)) # 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' def allow_link(): """Helper function to cross check if the user is authenticated, and it is has the 'subscriber' role. Also, we check if the node has world GET permissions, which means it's free. """ # Check if node permissions for the world exist (if node is free) if node.permissions and node.permissions.world: return 'GET' in node.permissions.world if current_user.is_authenticated(): allowed_roles = {u'subscriber', u'demo', u'admin'} return bool(allowed_roles.intersection(current_user.roles or ())) return False link_allowed = allow_link() node_type_handlers = { 'asset': _view_handler_asset, 'storage': _view_handler_storage, 'texture': _view_handler_texture, } if node_type_name in node_type_handlers: handler = node_type_handlers[node_type_name] template_path, template_action = handler(node, template_path, template_action, link_allowed) # Fetch linked resources. node.picture = get_file(node.picture, api=api) node.user = node.user and pillarsdk.User.find(node.user, api=api) node.parent = node.parent and pillarsdk.Node.find(node.parent, api=api) # Get children children_projection = {'project': 1, 'name': 1, 'picture': 1, 'parent': 1, 'node_type': 1, 'properties.order': 1, 'properties.status': 1, 'user': 1, 'properties.content_type': 1} children_where = {'parent': node._id} if node_type_name == 'group': children_where['properties.status'] = 'published' children_projection['permissions.world'] = 1 else: children_projection['properties.files'] = 1 children_projection['properties.is_tileable'] = 1 try: children = Node.all({ 'projection': children_projection, 'where': children_where, 'sort': [('properties.order', 1), ('name', 1)]}, api=api) except ForbiddenAccess: return render_template('errors/403_embed.html') children = children._items for child in children: child.picture = get_file(child.picture, api=api) if request.args.get('format') == 'json': node = node.to_dict() node['url_edit'] = url_for('nodes.edit', node_id=node['_id']), return jsonify({ 'node': node, 'children': children.to_dict(), 'parent': node.parent.to_dict() if node.parent else {} }) # Check if template exists on the filesystem template_path = '{0}/{1}_embed.html'.format(template_path, template_action) template_path_full = os.path.join(app.config['TEMPLATES_PATH'], template_path) if not os.path.exists(template_path_full): raise NotFound("Missing template '{0}'".format(template_path)) return render_template(template_path, node_id=node._id, node=node, parent=node.parent, children=children, config=app.config, api=api)
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 task_edit(): """We want to be able to edit the following properties: - status - owners - description - picture (optional) """ api = system_util.pillar_api() task_id = request.form['task_id'] task = Node.find(task_id, api=api) task.description = request.form['task_description'] if request.form['task_revision']: task.properties.revision = int(request.form['task_revision']) task.properties.status = request.form['task_status'] task.properties.filepath = request.form['task_filepath'] task.properties.owners.users = request.form.getlist('task_owners_users[]') siblings = Node.all( { 'where': 'parent==ObjectId("%s")' % task.parent, 'embedded': '{"picture":1, "user":1}' }, api=api) def check_conflict(task_current, task_sibling): return revsion_conflict[task_current.name](task_current, task_sibling) def task_animation(task_current, task_sibling): if task_sibling.name in [ 'fx_hair', 'fx_smoke', 'fx_grass', 'lighting' ]: if task_current.properties.revision > task_sibling.properties.revision: return True return False def task_lighting(task_current, task_sibling): if task_sibling.name in [ 'fx_hair', 'fx_smoke', 'fx_grass', 'animation' ]: if task_current.properties.revision < task_sibling.properties.revision: return True return False def task_fx_hair(task_current, task_sibling): if task_sibling.name in ['animation']: if task_current.properties.revision < task_sibling.properties.revision: return True if task_sibling.name in ['lighting']: if task_current.properties.revision > task_sibling.properties.revision: return True return False def task_fx_grass(task_current, task_sibling): pass def task_fx_smoke(task_current, task_sibling): pass revsion_conflict = { 'animation': task_animation, 'lighting': task_lighting, 'fx_hair': task_fx_hair, 'fx_grass': task_fx_grass, 'fx_smoke': task_fx_smoke } if task.properties.revision: for sibling in siblings._items: if sibling.properties.revision and sibling._id != task_id: if check_conflict(task, sibling) == True: task.properties.is_conflicting = True break else: task.properties.is_conflicting = False task.update(api=api) return jsonify(task.to_dict())
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 posts_view(project_id=None, project_url=None, url=None, *, archive=False, page=1): """View individual blogpost""" if bool(project_id) == bool(project_url): raise ValueError('posts_view(): pass either project_id or project_url') if url and archive: raise ValueError('posts_view(): cannot pass both url and archive') api = system_util.pillar_api() # Fetch project (for background images and links generation) if project_id: project = Project.find(project_id, api=api) else: project = Project.find_one({'where': {'url': project_url}}, api=api) project_id = project['_id'] attach_project_pictures(project, api) blog = Node.find_one( { 'where': { 'node_type': 'blog', 'project': project_id }, }, api=api) status_query = {} if blog.has_method('PUT') else { 'properties.status': 'published' } posts = Node.all( { 'where': { 'parent': blog._id, **status_query }, 'embedded': { 'user': 1 }, 'sort': '-_created', 'max_results': 20 if archive else 5, 'page': page, }, api=api) for post in posts._items: post.picture = get_file(post.picture, api=api) post.url = url_for_node(node=post) # Use the *_main_project.html template for the main blog is_main_project = project_id == current_app.config['MAIN_PROJECT_ID'] main_project_template = '_main_project' if is_main_project else '' main_project_template = '_main_project' index_arch = 'archive' if archive else 'index' template_path = f'nodes/custom/blog/{index_arch}{main_project_template}.html', if url: template_path = f'nodes/custom/post/view{main_project_template}.html', post = Node.find_one( { 'where': { 'parent': blog._id, 'properties.url': url }, 'embedded': { 'node_type': 1, 'user': 1 }, }, api=api) if post.picture: post.picture = get_file(post.picture, api=api) post.url = url_for_node(node=post) elif posts._items: post = posts._items[0] else: post = None if post is not None: # If post is not published, check that the user is also the author of # the post. If not, return an error. if post.properties.status != "published": if not (current_user.is_authenticated and post.has_method('PUT')): abort(403) can_create_blog_posts = project.node_type_has_method('post', 'POST', api=api) # Use functools.partial so we can later pass page=X. if is_main_project: url_func = functools.partial(url_for, 'main.main_blog_archive') else: url_func = functools.partial(url_for, 'main.project_blog_archive', project_url=project.url) project.blog_archive_url = url_func() pmeta = posts._meta seen_now = pmeta['max_results'] * pmeta['page'] if pmeta['total'] > seen_now: project.blog_archive_next = url_func(page=pmeta['page'] + 1) else: project.blog_archive_next = None if pmeta['page'] > 1: project.blog_archive_prev = url_func(page=pmeta['page'] - 1) else: project.blog_archive_prev = None title = 'blog_main' if is_main_project else 'blog' pages = Node.all( { 'where': { 'project': project._id, 'node_type': 'page' }, 'projection': { 'name': 1 } }, api=api) return render_template( template_path, blog=blog, node=post, posts=posts._items, posts_meta=pmeta, more_posts_available=pmeta['total'] > pmeta['max_results'], project=project, title=title, node_type_post=project.get_node_type('post'), can_create_blog_posts=can_create_blog_posts, pages=pages._items, api=api)