Пример #1
0
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
Пример #2
0
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()
Пример #3
0
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)
Пример #4
0
    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
Пример #5
0
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)
Пример #6
0
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()
Пример #7
0
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
Пример #8
0
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)
Пример #9
0
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
Пример #10
0
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)
Пример #11
0
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)
Пример #12
0
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
Пример #13
0
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
Пример #14
0
    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()
Пример #15
0
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
Пример #16
0
    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
Пример #17
0
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
Пример #18
0
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
Пример #19
0
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()
Пример #20
0
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]
Пример #21
0
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)
Пример #22
0
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())
Пример #23
0
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)
Пример #24
0
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)
Пример #25
0
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)
Пример #26
0
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)
Пример #27
0
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
Пример #28
0
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
Пример #29
0
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)
Пример #30
0
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)
Пример #31
0
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)
Пример #32
0
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())
Пример #33
0
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)
Пример #34
0
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
Пример #35
0
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())
Пример #36
0
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)
Пример #37
0
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)