Ejemplo n.º 1
0
def comments_create():
    content = request.form['content']
    parent_id = request.form.get('parent_id')
    api = system_util.pillar_api()
    parent_node = Node.find(parent_id, api=api)

    node_asset_props = dict(
        project=parent_node.project,
        name='Comment',
        user=current_user.objectid,
        node_type='comment',
        properties=dict(
            content=content,
            status='published',
            confidence=0,
            rating_positive=0,
            rating_negative=0))

    if parent_id:
        node_asset_props['parent'] = parent_id

    # Get the parent node and check if it's a comment. In which case we flag
    # the current comment as a reply.
    parent_node = Node.find(parent_id, api=api)
    if parent_node.node_type == 'comment':
        node_asset_props['properties']['is_reply'] = True

    node_asset = Node(node_asset_props)
    node_asset.create(api=api)

    return jsonify(
        asset_id=node_asset._id,
        content=node_asset.properties.content)
Ejemplo n.º 2
0
def comments_create():
    content = request.form['content']
    parent_id = request.form.get('parent_id')
    api = system_util.pillar_api()
    parent_node = Node.find(parent_id, api=api)

    node_asset_props = dict(project=parent_node.project,
                            name='Comment',
                            user=current_user.objectid,
                            node_type='comment',
                            properties=dict(content=content,
                                            status='published',
                                            confidence=0,
                                            rating_positive=0,
                                            rating_negative=0))

    if parent_id:
        node_asset_props['parent'] = parent_id

    # Get the parent node and check if it's a comment. In which case we flag
    # the current comment as a reply.
    parent_node = Node.find(parent_id, api=api)
    if parent_node.node_type == 'comment':
        node_asset_props['properties']['is_reply'] = True

    node_asset = Node(node_asset_props)
    node_asset.create(api=api)

    return jsonify(asset_id=node_asset._id,
                   content=node_asset.properties.content)
Ejemplo n.º 3
0
def jstree(node_id):
    """JsTree view.

    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>/jstree (construct the whole
      expanded tree starting from the node_id. Use only once)
    - https://pillar/<node_id>/jstree&children=1 (deliver the
      children of a node - use in the navigation of the tree)
    """

    # Get node with basic embedded data
    api = system_util.pillar_api()
    node = Node.find(node_id, api=api)

    if request.args.get('children') != '1':
        return jsonify(items=jstree_build_from_node(node))

    if node.node_type == '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)

    return jsonify(jstree_build_children(node))
Ejemplo n.º 4
0
def view_embed(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')

    project_projection = {'project': {'url': 1, 'name': 1}}
    project = Project.find(node.project, project_projection, api=api)

    node.picture = get_file(node.picture, api=api)
    node.user = node.user and User.find(node.user, api=api)

    write_access = 'PUT' in (node.allowed_methods or set())

    extra_template_args = {'project': project}

    return render_template(
        'nodes/custom/dillo_post/view_embed.html',
        node_id=node._id,
        node=node,
        write_access=write_access,
        api=api,
        **extra_template_args)
Ejemplo n.º 5
0
def comments_for_node(node_id):
    """Shows the comments attached to the given node."""

    api = system_util.pillar_api()

    node = Node.find(node_id, api=api)
    project = Project({'_id': node.project})
    can_post_comments = project.node_type_has_method('comment',
                                                     'POST',
                                                     api=api)
    can_comment_override = request.args.get('can_comment', 'True') == 'True'
    can_post_comments = can_post_comments and can_comment_override

    # Query for all children, i.e. comments on the node.
    comments = Node.all(
        {
            'where': {
                'node_type': 'comment',
                'parent': node_id
            },
        }, api=api)

    def enrich(some_comment):
        some_comment['_user'] = subquery.get_user_info(some_comment['user'])
        some_comment['_is_own'] = some_comment['user'] == current_user.objectid
        some_comment['_current_user_rating'] = None  # tri-state boolean
        some_comment[
            '_rating'] = some_comment.properties.rating_positive - some_comment.properties.rating_negative

        if current_user.is_authenticated:
            for rating in some_comment.properties.ratings or ():
                if rating.user != current_user.objectid:
                    continue

                some_comment['_current_user_rating'] = rating.is_positive

    for comment in comments['_items']:
        # Query for all grandchildren, i.e. replies to comments on the node.
        comment['_replies'] = Node.all(
            {
                'where': {
                    'node_type': 'comment',
                    'parent': comment['_id']
                },
            },
            api=api)

        enrich(comment)
        for reply in comment['_replies']['_items']:
            enrich(reply)

    nr_of_comments = sum(1 + comment['_replies']['_meta']['total']
                         for comment in comments['_items'])

    return render_template('nodes/custom/comment/list_embed.html',
                           node_id=node_id,
                           comments=comments,
                           nr_of_comments=nr_of_comments,
                           show_comments=True,
                           can_post_comments=can_post_comments)
Ejemplo n.º 6
0
def jstree(node_id):
    """JsTree view.

    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>/jstree (construct the whole
      expanded tree starting from the node_id. Use only once)
    - https://pillar/<node_id>/jstree&children=1 (deliver the
      children of a node - use in the navigation of the tree)
    """

    # Get node with basic embedded data
    api = system_util.pillar_api()
    node = Node.find(node_id, api=api)

    if request.args.get('children') != '1':
        return jsonify(items=jstree_build_from_node(node))

    if node.node_type == '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)

    return jsonify(jstree_build_children(node))
Ejemplo n.º 7
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
Ejemplo n.º 8
0
def find_for_comment(project, node):
    """Returns the URL for a comment."""

    api = system_util.pillar_api()

    parent = node
    while parent.node_type == 'comment':
        if isinstance(parent.parent, pillarsdk.Resource):
            parent = parent.parent
            continue

        try:
            parent = Node.find(parent.parent, api=api)
        except ResourceNotFound:
            log.warning(
                'url_for_node(node_id=%r): Unable to find parent node %r',
                node['_id'], parent.parent)
            raise ValueError('Unable to find parent node %r' % parent.parent)

    # Find the redirection URL for the parent node.
    parent_url = find_url_for_node(parent)
    if '#' in parent_url:
        # We can't attach yet another fragment, so just don't link to
        # the comment for now.
        return parent_url
    return parent_url + '#{}'.format(node['_id'])
Ejemplo n.º 9
0
    def load_latest(list_of_ids, node_type=None):
        """Loads a list of IDs in reversed order."""

        if not list_of_ids:
            return []

        # Construct query parameters outside the loop.
        projection = {'name': 1, 'user': 1, 'node_type': 1, 'project': 1,
                      'properties.url': 1, 'properties.content_type': 1,
                      'picture': 1}
        params = {'projection': projection, 'embedded': {'user': 1}}

        if node_type == 'post':
            projection['properties.content'] = 1
        elif node_type == 'asset':
            projection['description'] = 1

        list_latest = []
        for node_id in reversed(list_of_ids or ()):
            try:
                node_item = Node.find(node_id, params, api=api)

                node_item.picture = utils.get_file(node_item.picture, api=api)
                list_latest.append(node_item)
            except ForbiddenAccess:
                pass
            except ResourceNotFound:
                log.warning('Project %s refers to removed node %s!',
                            project._id, node_id)

        return list_latest
Ejemplo n.º 10
0
def add_featured_node():
    """Feature a node in a project. This method belongs here, because it affects
    the project node itself, not the asset.
    """
    api = system_util.pillar_api()
    node = Node.find(request.form['node_id'], api=api)
    action = project_update_nodes_list(node, list_name='featured')
    return jsonify(status='success', data=dict(action=action))
Ejemplo n.º 11
0
def add_featured_node():
    """Feature a node in a project. This method belongs here, because it affects
    the project node itself, not the asset.
    """
    api = system_util.pillar_api()
    node = Node.find(request.form['node_id'], api=api)
    action = project_update_nodes_list(node, project_id=node.project, list_name='featured')
    return jsonify(status='success', data=dict(action=action))
Ejemplo n.º 12
0
def projects_move_node():
    # Process the move action
    node_id = request.form['node_id']
    dest_parent_node_id = request.form['dest_parent_node_id']

    api = SystemUtility.attract_api()
    node = Node.find(node_id, api=api)
    node.parent = dest_parent_node_id
    node.update(api=api)
    return jsonify(status='success', data=dict(message='node moved'))
Ejemplo n.º 13
0
def delete_node():
    """Delete a node"""
    api = system_util.pillar_api()
    node = Node.find(request.form['node_id'], api=api)
    if not node.has_method('DELETE'):
        return abort(403)

    node.delete(api=api)

    return jsonify(status='success', data=dict(message='Node deleted'))
Ejemplo n.º 14
0
def delete_node():
    """Delete a node"""
    api = system_util.pillar_api()
    node = Node.find(request.form['node_id'], api=api)
    if not node.has_method('DELETE'):
        return abort(403)

    node.delete(api=api)

    return jsonify(status='success', data=dict(message='Node deleted'))
Ejemplo n.º 15
0
def comments_create():
    content = request.form['content']
    parent_id = request.form.get('parent_id')

    if not parent_id:
        log.warning('User %s tried to create comment without parent_id',
                    current_user.objectid)
        raise wz_exceptions.UnprocessableEntity()

    api = system_util.pillar_api()
    parent_node = Node.find(parent_id, api=api)
    if not parent_node:
        log.warning(
            'Unable to create comment for user %s, parent node %r not found',
            current_user.objectid, parent_id)
        raise wz_exceptions.UnprocessableEntity()

    log.info('Creating comment for user %s on parent node %r',
             current_user.objectid, parent_id)

    comment_props = dict(project=parent_node.project,
                         name='Comment',
                         user=current_user.objectid,
                         node_type='comment',
                         properties=dict(content=content,
                                         status='published',
                                         confidence=0,
                                         rating_positive=0,
                                         rating_negative=0))

    if parent_id:
        comment_props['parent'] = parent_id

    # Get the parent node and check if it's a comment. In which case we flag
    # the current comment as a reply.
    parent_node = Node.find(parent_id, api=api)
    if parent_node.node_type == 'comment':
        comment_props['properties']['is_reply'] = True

    comment = Node(comment_props)
    comment.create(api=api)

    return jsonify({'node_id': comment._id}), 201
Ejemplo n.º 16
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 ''

    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)
Ejemplo n.º 17
0
 def _find_parent(parent_node_id) -> Node:
     return Node.find(parent_node_id, {
         'projection': {
             '_id': 1,
             'name': 1,
             'node_type': 1,
             'project': 1,
             'parent': 1,
             'properties.url': 1,
         }
     },
                      api=api)
Ejemplo n.º 18
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)
Ejemplo n.º 19
0
def comments_for_node(node_id):
    """Shows the comments attached to the given node.

    The URL can be overridden in order to define can_post_comments in a different way
    """

    api = system_util.pillar_api()

    node = Node.find(node_id, api=api)
    project = Project({'_id': node.project})
    can_post_comments = project.node_type_has_method('comment', 'POST', api=api)
    can_comment_override = request.args.get('can_comment', 'True') == 'True'
    can_post_comments = can_post_comments and can_comment_override

    return render_comments_for_node(node_id, can_post_comments=can_post_comments)
Ejemplo n.º 20
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
Ejemplo n.º 21
0
def commentform_for_node(node_id):
    """Shows only the comment for for comments attached to the given node.

    i.e. does not show the comments themselves, just the form to post a new comment.
    """

    api = system_util.pillar_api()

    node = Node.find(node_id, api=api)
    project = Project({'_id': node.project})
    can_post_comments = project.node_type_has_method('comment', 'POST', api=api)

    return render_template('nodes/custom/comment/list_embed.html',
                           node_id=node_id,
                           show_comments=False,
                           can_post_comments=can_post_comments)
Ejemplo n.º 22
0
def move_node():
    """Move a node within a project. While this affects the node.parent prop, we
    keep it in the scope of the project."""
    node_id = request.form['node_id']
    dest_parent_node_id = request.form.get('dest_parent_node_id')

    api = system_util.pillar_api()
    node = Node.find(node_id, api=api)
    # Get original parent id for clearing template fragment on success
    previous_parent_id = node.parent
    if dest_parent_node_id:
        node.parent = dest_parent_node_id
    elif node.parent:
        node.parent = None
    node.update(api=api)
    return jsonify(status='success', data=dict(message='node moved'))
Ejemplo n.º 23
0
def toggle_node_public():
    """Give a node GET permissions for the world. Later on this can turn into
    a more powerful permission management function.
    """
    api = system_util.pillar_api()
    node = Node.find(request.form['node_id'], api=api)
    if node.has_method('PUT'):
        if node.permissions and 'world' in node.permissions.to_dict():
            node.permissions = {}
            message = "Node is not public anymore."
        else:
            node.permissions = dict(world=['GET'])
            message = "Node is now public!"
        node.update(api=api)
        return jsonify(status='success', data=dict(message=message))
    else:
        return abort(403)
Ejemplo n.º 24
0
def delete(node_id):
    """Generic node deletion
    """
    api = SystemUtility.attract_api()
    node = Node.find(node_id, api=api)
    name = node.name
    node_type = NodeType.find(node.node_type, api=api)
    try:
        node.delete(api=api)
        forbidden = False
    except ForbiddenAccess:
        forbidden = True

    if not forbidden:
        # print (node_type['name'])
        return redirect(url_for('nodes.index', node_type_name=node_type['name']))
    else:
        return redirect(url_for('nodes.edit', node_id=node._id))
Ejemplo n.º 25
0
def url_for_node(node_id=None, node=None):
    assert isinstance(node_id, (str, type(None)))

    api = system_util.pillar_api()

    if node_id is None and node is None:
        raise ValueError('Either node or node_id must be given')

    if node is None:
        try:
            node = Node.find(node_id, api=api)
        except ResourceNotFound:
            log.warning(
                'url_for_node(node_id=%r, node=None): Unable to find node.',
                node_id)
            raise wz_exceptions.NotFound('Unable to find node %r' % node_id)

    return finders.find_url_for_node(node)
Ejemplo n.º 26
0
def posts_edit(post_id):
    api = system_util.pillar_api()

    try:
        post = Node.find(post_id, {'embedded': '{"user": 1}'}, api=api)
    except ResourceNotFound:
        return abort(404)
    # Check if user is allowed to edit the post
    if not post.has_method('PUT'):
        return abort(403)

    project = Project.find(post.project, api=api)
    attach_project_pictures(project, api)

    node_type = project.get_node_type(post.node_type)
    form = get_node_form(node_type)
    if form.validate_on_submit():
        if process_node_form(form,
                             node_id=post_id,
                             node_type=node_type,
                             user=current_user.objectid):
            # The the post is published, add it to the list
            if form.status.data == 'published':
                project_update_nodes_list(post,
                                          project_id=project._id,
                                          list_name='blog')
            return redirect(url_for_node(node=post))
    form.parent.data = post.parent
    form.name.data = post.name
    form.content.data = post.properties.content
    form.status.data = post.properties.status
    form.url.data = post.properties.url
    if post.picture:
        form.picture.data = post.picture
        # Embed picture file
        post.picture = get_file(post.picture, api=api)
    if post.properties.picture_square:
        form.picture_square.data = post.properties.picture_square
    return render_template('nodes/custom/post/edit.html',
                           node_type=node_type,
                           post=post,
                           form=form,
                           project=project,
                           api=api)
Ejemplo n.º 27
0
def delete(node_id):
    """Generic node deletion
    """
    api = system_util.pillar_api()
    node = Node.find(node_id, api=api)
    name = node.name
    node_type = NodeType.find(node.node_type, api=api)
    try:
        node.delete(api=api)
        forbidden = False
    except ForbiddenAccess:
        forbidden = True

    if not forbidden:
        # print (node_type['name'])
        return redirect(
            url_for('nodes.index', node_type_name=node_type['name']))
    else:
        return redirect(url_for('nodes.edit', node_id=node._id))
Ejemplo n.º 28
0
def toggle_node_public():
    """Give a node GET permissions for the world. Later on this can turn into
    a more powerful permission management function.
    """
    api = system_util.pillar_api()
    node = Node.find(request.form['node_id'], api=api)
    if node.has_method('PUT'):
        if node.permissions and 'world' in node.permissions.to_dict():
            node.permissions = {}
            message = "Node is not public anymore."
        else:
            node.permissions = dict(world=['GET'])
            message = "Node is now public!"
        node.update(api=api)
        # Delete cached parent template fragment
        delete_redis_cache_template('group_view', node.parent)
        return jsonify(status='success', data=dict(message=message))
    else:
        return abort(403)
Ejemplo n.º 29
0
def shot_edit():
    """We want to be able to edit the following properties:
    - notes
    - status
    - cut in
    - cut out
    - picture (optional)
    """
    api = system_util.pillar_api()
    shot_id = request.form['shot_id']

    shot = Node.find(shot_id, api=api)
    shot.properties.notes = request.form['shot_notes']
    shot.properties.status = request.form['shot_status']
    shot.properties.cut_in = int(request.form['shot_cut_in'])
    shot.properties.cut_out = int(request.form['shot_cut_out'])

    shot.update(api=api)

    return jsonify(shot.to_dict())
Ejemplo n.º 30
0
def shot_edit():
    """We want to be able to edit the following properties:
    - notes
    - status
    - cut in
    - cut out
    - picture (optional)
    """
    api = system_util.pillar_api()
    shot_id = request.form['shot_id']

    shot = Node.find(shot_id, api=api)
    shot.properties.notes = request.form['shot_notes']
    shot.properties.status = request.form['shot_status']
    shot.properties.cut_in = int(request.form['shot_cut_in'])
    shot.properties.cut_out = int(request.form['shot_cut_out'])

    shot.update(api=api)

    return jsonify(shot.to_dict())
Ejemplo n.º 31
0
def move_node():
    """Move a node within a project. While this affects the node.parent prop, we
    keep it in the scope of the project."""
    node_id = request.form['node_id']
    dest_parent_node_id = request.form.get('dest_parent_node_id')

    api = system_util.pillar_api()
    node = Node.find(node_id, api=api)
    # Get original parent id for clearing template fragment on success
    previous_parent_id = node.parent
    if dest_parent_node_id:
        node.parent = dest_parent_node_id
    elif node.parent:
        node.parent = None
    node.update(api=api)
    # Delete cached parent template fragment
    if node.parent:
        delete_redis_cache_template('group_view', node.parent)
    if previous_parent_id:
        delete_redis_cache_template('group_view', previous_parent_id)
    return jsonify(status='success', data=dict(message='node moved'))
Ejemplo n.º 32
0
    def find_for_comment():
        """Returns the URL for a comment."""

        parent = node
        while parent.node_type == 'comment':
            if isinstance(parent.parent, pillarsdk.Resource):
                parent = parent.parent
                continue

            try:
                parent = Node.find(parent.parent, api=api)
            except ResourceNotFound:
                log.warning('url_for_node(node_id=%r): Unable to find parent node %r',
                            node_id, parent.parent)
                raise ValueError('Unable to find parent node %r' % parent.parent)

        # Find the redirection URL for the parent node.
        parent_url = url_for_node(node=parent)
        if '#' in parent_url:
            # We can't attach yet another fragment, so just don't link to the comment for now.
            return parent_url
        return parent_url + '#{}'.format(node_id)
Ejemplo n.º 33
0
def project_update_nodes_list(node_id, project_id=None, list_name='latest'):
    """Update the project node with the latest edited or favorited node.
    The list value can be 'latest' or 'featured' and it will determined where
    the node reference will be placed in.
    """
    if not project_id and 'current_project_id' in session:
        project_id = session['current_project_id']
    elif not project_id:
        return None
    api = SystemUtility.attract_api()
    project = Node.find(project_id, api=api)
    if list_name == 'latest':
        nodes_list = project.properties.nodes_latest
    else:
        nodes_list = project.properties.nodes_featured

    # Do not allow adding project to lists
    if node_id == project._id:
        return "fail"

    if not nodes_list:
        node_list_name = 'nodes_' + list_name
        project.properties[node_list_name] = []
        nodes_list = project.properties[node_list_name]
    elif len(nodes_list) > 5:
        nodes_list.pop(0)

    if node_id in nodes_list:
        # Pop to put this back on top of the list
        nodes_list.remove(node_id)
        if list_name == 'featured':
            # We treat the action as a toggle and do not att the item back
            project.update(api=api)
            return "removed"

    nodes_list.append(node_id)
    project.update(api=api)
    return "added"
Ejemplo n.º 34
0
def project_update_nodes_list(node_id, project_id=None, list_name='latest'):
    """Update the project node with the latest edited or favorited node.
    The list value can be 'latest' or 'featured' and it will determined where
    the node reference will be placed in.
    """
    if not project_id and 'current_project_id' in session:
        project_id = session['current_project_id']
    elif not project_id:
        return None
    api = SystemUtility.attract_api()
    project = Node.find(project_id, api=api)
    if list_name == 'latest':
        nodes_list = project.properties.nodes_latest
    else:
        nodes_list = project.properties.nodes_featured

    # Do not allow adding project to lists
    if node_id == project._id:
        return "fail"

    if not nodes_list:
        node_list_name = 'nodes_' + list_name
        project.properties[node_list_name] = []
        nodes_list = project.properties[node_list_name]
    elif len(nodes_list) > 5:
        nodes_list.pop(0)

    if node_id in nodes_list:
        # Pop to put this back on top of the list
        nodes_list.remove(node_id)
        if list_name == 'featured':
            # We treat the action as a toggle and do not att the item back
            project.update(api=api)
            return "removed"

    nodes_list.append(node_id)
    project.update(api=api)
    return "added"
Ejemplo n.º 35
0
def toggle_node_project_header():
    """Sets this node as the project header, or removes it if already there.
    """

    api = system_util.pillar_api()
    node_id = request.form['node_id']

    try:
        node = Node.find(node_id, {'projection': {'project': 1}}, api=api)
    except ResourceNotFound:
        log.info(
            'User %s trying to toggle non-existing node %s as project header',
            current_user.objectid, node_id)
        return jsonify(_status='ERROR', message='Node not found'), 404

    try:
        project = Project.find(node.project, api=api)
    except ResourceNotFound:
        log.info(
            'User %s trying to toggle node %s as project header, but project %s not found',
            current_user.objectid, node_id, node.project)
        return jsonify(_status='ERROR', message='Project not found'), 404

    # Toggle header node
    if project.header_node == node_id:
        log.debug('Un-setting header node of project %s', node.project)
        project.header_node = None
        action = 'unset'
    else:
        log.debug('Setting node %s as header of project %s', node_id,
                  node.project)
        project.header_node = node_id
        action = 'set'

    # Save the project
    project.update(api=api)

    return jsonify({'_status': 'OK', 'action': action})
Ejemplo n.º 36
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)
Ejemplo n.º 37
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())
Ejemplo n.º 38
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)
Ejemplo n.º 39
0
def edit(node_id):
    """Generic node editing form
    """

    def set_properties(dyn_schema, form_schema, node_properties, form,
                       prefix="",
                       set_data=True):
        """Initialize custom properties for the form. We run this function once
        before validating the function with set_data=False, so that we can set
        any multiselect field that was originally specified empty and fill it
        with the current choices.
        """
        for prop in dyn_schema:
            schema_prop = dyn_schema[prop]
            form_prop = form_schema[prop]
            prop_name = "{0}{1}".format(prefix, prop)

            if schema_prop['type'] == 'dict':
                set_properties(
                    schema_prop['schema'],
                    form_prop['schema'],
                    node_properties[prop_name],
                    form,
                    "{0}__".format(prop_name))
                continue

            if prop_name not in form:
                continue

            try:
                db_prop_value = node_properties[prop]
            except KeyError:
                log.debug('%s not found in form for node %s', prop_name, node_id)
                continue

            if schema_prop['type'] == 'datetime':
                db_prop_value = datetime.strptime(db_prop_value,
                                                  app.config['RFC1123_DATE_FORMAT'])

            if isinstance(form[prop_name], SelectMultipleField):
                # If we are dealing with a multiselect field, check if
                # it's empty (usually because we can't query the whole
                # database to pick all the choices). If it's empty we
                # populate the choices with the actual data.
                if not form[prop_name].choices:
                    form[prop_name].choices = [(d, d) for d in db_prop_value]
                    # Choices should be a tuple with value and name

            # Assign data to the field
            if set_data:
                if prop_name == 'attachments':
                    for attachment_collection in db_prop_value:
                        for a in attachment_collection['files']:
                            attachment_form = ProceduralFileSelectForm()
                            attachment_form.file = a['file']
                            attachment_form.slug = a['slug']
                            attachment_form.size = 'm'
                            form[prop_name].append_entry(attachment_form)

                elif prop_name == 'files':
                    schema = schema_prop['schema']['schema']
                    # Extra entries are caused by min_entries=1 in the form
                    # creation.
                    field_list = form[prop_name]
                    if len(db_prop_value) > 0:
                        while len(field_list):
                            field_list.pop_entry()

                    for file_data in db_prop_value:
                        file_form_class = build_file_select_form(schema)
                        subform = file_form_class()
                        for key, value in file_data.iteritems():
                            setattr(subform, key, value)
                        field_list.append_entry(subform)

                # elif prop_name == 'tags':
                #     form[prop_name].data = ', '.join(data)
                else:
                    form[prop_name].data = db_prop_value
            else:
                # Default population of multiple file form list (only if
                # we are getting the form)
                if request.method == 'POST':
                    continue
                if prop_name == 'attachments':
                    if not db_prop_value:
                        attachment_form = ProceduralFileSelectForm()
                        attachment_form.file = 'file'
                        attachment_form.slug = ''
                        attachment_form.size = ''
                        form[prop_name].append_entry(attachment_form)

    api = system_util.pillar_api()
    node = Node.find(node_id, api=api)
    project = Project.find(node.project, api=api)
    node_type = project.get_node_type(node.node_type)
    form = get_node_form(node_type)
    user_id = current_user.objectid
    dyn_schema = node_type['dyn_schema'].to_dict()
    form_schema = node_type['form_schema'].to_dict()
    error = ""

    node_properties = node.properties.to_dict()

    ensure_lists_exist_as_empty(node.to_dict(), node_type)
    set_properties(dyn_schema, form_schema, node_properties, form,
                   set_data=False)

    if form.validate_on_submit():
        if process_node_form(form, node_id=node_id, node_type=node_type, user=user_id):
            # Handle the specific case of a blog post
            if node_type.name == 'post':
                project_update_nodes_list(node, list_name='blog')
            else:
                project_update_nodes_list(node)
            # Emergency hardcore cache flush
            # cache.clear()
            return redirect(url_for('nodes.view', node_id=node_id, embed=1,
                                    _external=True,
                                    _scheme=app.config['SCHEME']))
        else:
            log.debug('Error sending data to Pillar, see Pillar logs.')
            error = 'Server error'
    else:
        if form.errors:
            log.debug('Form errors: %s', form.errors)

    # Populate Form
    form.name.data = node.name
    form.description.data = node.description
    if 'picture' in form:
        form.picture.data = node.picture
    if node.parent:
        form.parent.data = node.parent

    set_properties(dyn_schema, form_schema, node_properties, form)

    # Get previews
    node.picture = get_file(node.picture, api=api) if node.picture else None

    # Get Parent
    try:
        parent = Node.find(node['parent'], api=api)
    except KeyError:
        parent = None
    except ResourceNotFound:
        parent = None

    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'

    template = '{0}/edit{1}.html'.format(node_type['name'], embed_string)

    # We should more simply check if the template file actually exsists on
    # the filesystem level
    try:
        return render_template(
            template,
            node=node,
            parent=parent,
            form=form,
            errors=form.errors,
            error=error,
            api=api)
    except TemplateNotFound:
        template = 'nodes/edit{1}.html'.format(node_type['name'], embed_string)
        return render_template(
            template,
            node=node,
            parent=parent,
            form=form,
            errors=form.errors,
            error=error,
            api=api)
Ejemplo n.º 40
0
def get_node(node_id, user_id):
    api = system_util.pillar_api()
    node = Node.find(node_id + '/?embedded={"node_type":1}', api=api)
    return node.to_dict()
Ejemplo n.º 41
0
def edit(node_id):
    """Generic node editing form, displayed only if the user is allowed.
    """
    def set_properties(dyn_schema,
                       form_schema,
                       node_properties,
                       form,
                       set_data,
                       prefix=""):
        """Initialize custom properties for the form. We run this function once
        before validating the function with set_data=False, so that we can set
        any multiselect field that was originally specified empty and fill it
        with the current choices.
        """

        log.debug('set_properties(..., prefix=%r, set_data=%r) called', prefix,
                  set_data)

        for prop, schema_prop in dyn_schema.items():
            prop_name = "{0}{1}".format(prefix, prop)

            if prop_name not in form:
                continue

            try:
                db_prop_value = node_properties[prop]
            except KeyError:
                log.debug('%s not found in form for node %s', prop_name,
                          node_id)
                continue

            if schema_prop['type'] == 'datetime':
                db_prop_value = datetime.strptime(
                    db_prop_value, current_app.config['RFC1123_DATE_FORMAT'])

            if isinstance(form[prop_name], SelectMultipleField):
                # If we are dealing with a multiselect field, check if
                # it's empty (usually because we can't query the whole
                # database to pick all the choices). If it's empty we
                # populate the choices with the actual data.
                if not form[prop_name].choices:
                    form[prop_name].choices = [(d, d) for d in db_prop_value]
                    # Choices should be a tuple with value and name
            if not set_data:
                continue

            # Assign data to the field
            if prop_name == 'attachments':
                # If attachments is an empty list, do not append data
                if not db_prop_value:
                    continue
                attachments.attachment_form_group_set_data(
                    db_prop_value, schema_prop, form[prop_name])
            elif prop_name == 'files':
                subschema = schema_prop['schema']['schema']
                # Extra entries are caused by min_entries=1 in the form
                # creation.
                field_list = form[prop_name]
                if len(db_prop_value):
                    while len(field_list):
                        field_list.pop_entry()

                for file_data in db_prop_value:
                    file_form_class = build_file_select_form(subschema)
                    subform = file_form_class()
                    for key, value in file_data.items():
                        setattr(subform, key, value)
                    field_list.append_entry(subform)

            # elif prop_name == 'tags':
            #     form[prop_name].data = ', '.join(data)
            else:
                form[prop_name].data = db_prop_value

    api = system_util.pillar_api()
    node = Node.find(node_id, api=api)

    # We do not want to display the page to users who can't PUT
    if 'PUT' not in node.allowed_methods:
        raise wz_exceptions.Forbidden()

    project = Project.find(node.project, api=api)
    node_type = project.get_node_type(node.node_type)
    form = get_node_form(node_type)
    user_id = current_user.objectid
    dyn_schema = node_type['dyn_schema'].to_dict()
    form_schema = node_type['form_schema'].to_dict()
    error = ""
    node_properties = node.properties.to_dict()

    ensure_lists_exist_as_empty(node.to_dict(), node_type)
    set_properties(dyn_schema,
                   form_schema,
                   node_properties,
                   form,
                   set_data=False)

    if form.validate_on_submit():
        if process_node_form(form,
                             node_id=node_id,
                             node_type=node_type,
                             user=user_id):
            # Handle the specific case of a blog post
            if node_type.name == 'post':
                project_update_nodes_list(node,
                                          project_id=project._id,
                                          list_name='blog')
            else:
                try:
                    project_update_nodes_list(node, project_id=project._id)
                except ForbiddenAccess:
                    # TODO (fsiddi): Implement this as a blender-cloud-only hook
                    log.debug(
                        'User %s not allowed to update latest_nodes in %s' %
                        (user_id, project._id))
            return redirect(
                url_for('nodes.view',
                        node_id=node_id,
                        embed=1,
                        _external=True,
                        _scheme=current_app.config['SCHEME']))
        else:
            log.debug('Error sending data to Pillar, see Pillar logs.')
            error = 'Server error'
    else:
        if form.errors:
            log.debug('Form errors: %s', form.errors)
    # Populate Form
    form.name.data = node.name
    form.description.data = node.description
    if 'picture' in form:
        form.picture.data = node.picture
    if node.parent:
        form.parent.data = node.parent
    set_properties(dyn_schema,
                   form_schema,
                   node_properties,
                   form,
                   set_data=True)

    # Get previews
    node.picture = get_file(node.picture, api=api) if node.picture else None

    # Get Parent
    try:
        parent = Node.find(node['parent'], api=api)
    except KeyError:
        parent = None
    except ResourceNotFound:
        parent = None

    embed_string = ''
    # Check if we want to embed the content via an AJAX call
    if request.args.get('embed') == '1':
        # Define the prefix for the embedded template
        embed_string = '_embed'
    else:
        attach_project_pictures(project, api)

    template = 'nodes/custom/{0}/edit{1}.html'.format(node_type['name'],
                                                      embed_string)
    # We should more simply check if the template file actually exists on the filesystem
    try:
        return render_template(
            template,
            node=node,
            parent=parent,
            form=form,
            errors=form.errors,
            error=error,
            api=api,
            project=project,
        )
    except TemplateNotFound:
        template = 'nodes/edit{1}.html'.format(node_type['name'], embed_string)
        is_embedded_edit = True if embed_string else False
        return render_template(
            template,
            node=node,
            parent=parent,
            form=form,
            errors=form.errors,
            error=error,
            api=api,
            project=project,
            is_embedded_edit=is_embedded_edit,
        )
Ejemplo n.º 42
0
def edit(node_id):
    """Generic node editing form
    """
    def set_properties(dyn_schema,
                       form_schema,
                       node_properties,
                       form,
                       prefix="",
                       set_data=True):
        """Initialize custom properties for the form. We run this function once
        before validating the function with set_data=False, so that we can set
        any multiselect field that was originally specified empty and fill it
        with the current choices.
        """
        for prop in dyn_schema:
            schema_prop = dyn_schema[prop]
            form_prop = form_schema[prop]
            prop_name = "{0}{1}".format(prefix, prop)

            if schema_prop['type'] == 'dict':
                set_properties(schema_prop['schema'], form_prop['schema'],
                               node_properties[prop_name], form,
                               "{0}__".format(prop_name))
                continue

            if prop_name not in form:
                continue

            try:
                db_prop_value = node_properties[prop]
            except KeyError:
                log.debug('%s not found in form for node %s', prop_name,
                          node_id)
                continue

            if schema_prop['type'] == 'datetime':
                db_prop_value = datetime.strptime(
                    db_prop_value, app.config['RFC1123_DATE_FORMAT'])

            if isinstance(form[prop_name], SelectMultipleField):
                # If we are dealing with a multiselect field, check if
                # it's empty (usually because we can't query the whole
                # database to pick all the choices). If it's empty we
                # populate the choices with the actual data.
                if not form[prop_name].choices:
                    form[prop_name].choices = [(d, d) for d in db_prop_value]
                    # Choices should be a tuple with value and name

            # Assign data to the field
            if set_data:
                if prop_name == 'attachments':
                    for attachment_collection in db_prop_value:
                        for a in attachment_collection['files']:
                            attachment_form = ProceduralFileSelectForm()
                            attachment_form.file = a['file']
                            attachment_form.slug = a['slug']
                            attachment_form.size = 'm'
                            form[prop_name].append_entry(attachment_form)

                elif prop_name == 'files':
                    schema = schema_prop['schema']['schema']
                    # Extra entries are caused by min_entries=1 in the form
                    # creation.
                    field_list = form[prop_name]
                    if len(db_prop_value) > 0:
                        while len(field_list):
                            field_list.pop_entry()

                    for file_data in db_prop_value:
                        file_form_class = build_file_select_form(schema)
                        subform = file_form_class()
                        for key, value in file_data.iteritems():
                            setattr(subform, key, value)
                        field_list.append_entry(subform)

                # elif prop_name == 'tags':
                #     form[prop_name].data = ', '.join(data)
                else:
                    form[prop_name].data = db_prop_value
            else:
                # Default population of multiple file form list (only if
                # we are getting the form)
                if request.method == 'POST':
                    continue
                if prop_name == 'attachments':
                    if not db_prop_value:
                        attachment_form = ProceduralFileSelectForm()
                        attachment_form.file = 'file'
                        attachment_form.slug = ''
                        attachment_form.size = ''
                        form[prop_name].append_entry(attachment_form)

    api = system_util.pillar_api()
    node = Node.find(node_id, api=api)
    project = Project.find(node.project, api=api)
    node_type = project.get_node_type(node.node_type)
    form = get_node_form(node_type)
    user_id = current_user.objectid
    dyn_schema = node_type['dyn_schema'].to_dict()
    form_schema = node_type['form_schema'].to_dict()
    error = ""

    node_properties = node.properties.to_dict()

    ensure_lists_exist_as_empty(node.to_dict(), node_type)
    set_properties(dyn_schema,
                   form_schema,
                   node_properties,
                   form,
                   set_data=False)

    if form.validate_on_submit():
        if process_node_form(form,
                             node_id=node_id,
                             node_type=node_type,
                             user=user_id):
            # Handle the specific case of a blog post
            if node_type.name == 'post':
                project_update_nodes_list(node, list_name='blog')
            else:
                project_update_nodes_list(node)
            # Emergency hardcore cache flush
            # cache.clear()
            return redirect(
                url_for('nodes.view',
                        node_id=node_id,
                        embed=1,
                        _external=True,
                        _scheme=app.config['SCHEME']))
        else:
            log.debug('Error sending data to Pillar, see Pillar logs.')
            error = 'Server error'
    else:
        if form.errors:
            log.debug('Form errors: %s', form.errors)

    # Populate Form
    form.name.data = node.name
    form.description.data = node.description
    if 'picture' in form:
        form.picture.data = node.picture
    if node.parent:
        form.parent.data = node.parent

    set_properties(dyn_schema, form_schema, node_properties, form)

    # Get previews
    node.picture = get_file(node.picture, api=api) if node.picture else None

    # Get Parent
    try:
        parent = Node.find(node['parent'], api=api)
    except KeyError:
        parent = None
    except ResourceNotFound:
        parent = None

    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'

    template = '{0}/edit{1}.html'.format(node_type['name'], embed_string)

    # We should more simply check if the template file actually exsists on
    # the filesystem level
    try:
        return render_template(template,
                               node=node,
                               parent=parent,
                               form=form,
                               errors=form.errors,
                               error=error,
                               api=api)
    except TemplateNotFound:
        template = 'nodes/edit{1}.html'.format(node_type['name'], embed_string)
        return render_template(template,
                               node=node,
                               parent=parent,
                               form=form,
                               errors=form.errors,
                               error=error,
                               api=api)
Ejemplo n.º 43
0
def breadcrumbs(node_id: str):
    """Return breadcrumbs for the given node, as JSON.

    Note that a missing parent is still returned in the breadcrumbs,
    but with `{_exists: false, name: '-unknown-'}`.

    The breadcrumbs start with the top-level parent, and end with the node
    itself (marked by {_self: true}). Returns JSON like this:

    {breadcrumbs: [
        ...,
        {_id: "parentID",
         name: "The Parent Node",
         node_type: "group",
         url: "/p/project/parentID"},
        {_id: "deadbeefbeefbeefbeeffeee",
         _self: true,
         name: "The Node Itself",
         node_type: "asset",
         url: "/p/project/nodeID"},
    ]}

    When a parent node is missing, it has a breadcrumb like this:

    {_id: "deadbeefbeefbeefbeeffeee",
     _exists': false,
     name': '-unknown-'}
    """

    api = system_util.pillar_api()
    is_self = True

    def make_crumb(some_node: None) -> dict:
        """Construct a breadcrumb for this node."""
        nonlocal is_self

        crumb = {
            '_id': some_node._id,
            'name': some_node.name,
            'node_type': some_node.node_type,
            'url': finders.find_url_for_node(some_node),
        }
        if is_self:
            crumb['_self'] = True
            is_self = False
        return crumb

    def make_missing_crumb(some_node_id: None) -> dict:
        """Construct 'missing parent' breadcrumb."""

        return {
            '_id': some_node_id,
            '_exists': False,
            'name': '-unknown-',
        }

    # The first node MUST exist.
    try:
        node = Node.find(node_id, api=api)
    except ResourceNotFound:
        log.warning('breadcrumbs(node_id=%r): Unable to find node', node_id)
        raise wz_exceptions.NotFound(f'Unable to find node {node_id}')
    except ForbiddenAccess:
        log.warning('breadcrumbs(node_id=%r): access denied to current user',
                    node_id)
        raise wz_exceptions.Forbidden(f'No access to node {node_id}')

    crumbs = []
    while True:
        crumbs.append(make_crumb(node))

        child_id = node._id
        node_id = node.parent
        if not node_id:
            break

        # If a subsequent node doesn't exist any more, include that in the breadcrumbs.
        # Forbidden nodes are handled as if they don't exist.
        try:
            node = Node.find(node_id, api=api)
        except (ResourceNotFound, ForbiddenAccess):
            log.warning(
                'breadcrumbs: Unable to find node %r but it is marked as parent of %r',
                node_id, child_id)
            crumbs.append(make_missing_crumb(node_id))
            break

    return jsonify({'breadcrumbs': list(reversed(crumbs))})
Ejemplo n.º 44
0
def url_for_node(node_id=None, node=None):
    assert isinstance(node_id, (basestring, type(None)))
    # assert isinstance(node, (Node, type(None))), 'wrong type for node: %r' % type(node)

    api = system_util.pillar_api()

    # Find node by its ID, or the ID by the node, depending on what was passed as parameters.
    if node is None:
        try:
            node = Node.find(node_id, api=api)
        except ResourceNotFound:
            log.warning('url_for_node(node_id=%r, node=None): Unable to find node.', node_id)
            raise ValueError('Unable to find node %r' % node_id)
    elif node_id is None:
        node_id = node['_id']
    else:
        raise ValueError('Either node or node_id must be given')

    # Find the node's project, or its ID, depending on whether a project was embedded.
    # This is needed in two of the three finder functions.
    project_id = node.project
    if isinstance(project_id, pillarsdk.Resource):
        # Embedded project
        project = project_id
        project_id = project['_id']
    else:
        project = None

    def project_or_error():
        """Returns the project, raising a ValueError if it can't be found."""

        if project is not None:
            return project

        try:
            return Project.find(project_id, {'projection': {'url': 1}}, api=api)
        except ResourceNotFound:
            log.warning('url_for_node(node_id=%r): Unable to find project %r',
                        node_id, project_id)
            raise ValueError('Unable to find node project %r' % project_id)

    def find_for_comment():
        """Returns the URL for a comment."""

        parent = node
        while parent.node_type == 'comment':
            if isinstance(parent.parent, pillarsdk.Resource):
                parent = parent.parent
                continue

            try:
                parent = Node.find(parent.parent, api=api)
            except ResourceNotFound:
                log.warning('url_for_node(node_id=%r): Unable to find parent node %r',
                            node_id, parent.parent)
                raise ValueError('Unable to find parent node %r' % parent.parent)

        # Find the redirection URL for the parent node.
        parent_url = url_for_node(node=parent)
        if '#' in parent_url:
            # We can't attach yet another fragment, so just don't link to the comment for now.
            return parent_url
        return parent_url + '#{}'.format(node_id)

    def find_for_post():
        """Returns the URL for a blog post."""

        if str(project_id) == app.config['MAIN_PROJECT_ID']:
            return url_for('main_blog',
                           url=node.properties.url)

        return url_for('project_blog',
                       project_url=project_or_error().url,
                       url=node.properties.url)

    # Fallback: Assets, textures, and other node types.
    def find_for_other():
        return url_for('projects.view',
                       project_url=project_or_error().url) + '#{}'.format(node_id)

    # Determine which function to use to find the correct URL.
    url_finders = {
        'comment': find_for_comment,
        'post': find_for_post,
    }

    finder = url_finders.get(node.node_type, find_for_other)
    return finder()
Ejemplo n.º 45
0
def get_node(node_id, user_id):
    api = system_util.pillar_api()
    node = Node.find(node_id + '/?embedded={"node_type":1}', api=api)
    return node.to_dict()
Ejemplo n.º 46
0
def view(project_url):
    """Entry point to view a project"""
    api = system_util.pillar_api()
    # Fetch the Node or 404
    try:
        project = Project.find_one({'where': {"url": project_url}}, api=api)
    except ResourceNotFound:
        abort(404)
    # Set up variables for processing
    user_id = 'ANONYMOUS' if current_user.is_anonymous() else str(current_user.objectid)
    rewrite_url = None
    embedded_node_id = None

    if request.args.get('redir') and request.args.get('redir') == '1':
        # Handle special cases (will be mainly used for items that are part
        # of the blog, or attract)
        if g.get('embedded_node')['node_type'] == 'post':
            # Very special case of the post belonging to the main project,
            # which is read from the configuration.
            if project._id == app.config['MAIN_PROJECT_ID']:
                return redirect(url_for('main_blog',
                    url=g.get('embedded_node')['properties']['url']))
            else:
                return redirect(url_for('project_blog',
                    project_url=project.url,
                    url=g.get('embedded_node')['properties']['url']))
        rewrite_url = "/p/{0}/#{1}".format(project.url,
            g.get('embedded_node')['_id'])
        embedded_node_id = g.get('embedded_node')['_id']

    if request.args.get('format') == 'jstree':
        return jsonify(items=jstree_get_children(None, project._id))

    project.picture_square = project.picture_square and get_file(project.picture_square, api=api)
    project.picture_header = project.picture_header and get_file(project.picture_header, api=api)
    embed_string = ''

    if request.args.get('embed'):
        embed_string = '_embed'
        list_latest = []
        if project.nodes_latest:
            for node_id in project.nodes_latest:
                try:
                    node_item = Node.find(node_id, {
                        'projection': '{"name":1, "user":1, "node_type":1, \
                            "project": 1}',
                        'embedded': '{"user":1}',
                        }, api=api)
                    list_latest.append(node_item)
                except ForbiddenAccess:
                    pass
        project.nodes_latest = list(reversed(list_latest))

        list_featured = []
        if project.nodes_featured:
            for node_id in project.nodes_featured:
                try:
                    node_item = Node.find(node_id, {
                        'projection': '{"name":1, "user":1, "picture":1, \
                            "node_type":1, "project": 1}',
                        'embedded': '{"user":1}',
                        }, api=api)
                    if node_item.picture:
                        picture = get_file(node_item.picture, api=api)
                        # picture = File.find(node_item.picture, api=api)
                        node_item.picture = picture
                    list_featured.append(node_item)
                except ForbiddenAccess:
                    pass
        project.nodes_featured = list(reversed(list_featured))

        list_blog = []
        if project.nodes_blog:
            for node_id in project.nodes_blog:
                try:
                    node_item = Node.find(node_id, {
                        # 'projection': '{"name":1, "user":1, "node_type":1}',
                        'embedded': '{"user":1}',
                        }, api=api)
                    list_blog.append(node_item)
                except ForbiddenAccess:
                    pass
        project.nodes_blog = list(reversed(list_blog))

    return render_template("projects/view{0}.html".format(embed_string),
                           embedded_node_id=embedded_node_id,
                           rewrite_url=rewrite_url,
                           user_string_id=user_id,
                           project=project,
                           api=api)
Ejemplo n.º 47
0
def jstree_build_from_node(node):
    api = SystemUtility.attract_api()
    open_nodes = [jstree_parse_node(node)]
    # Get the current node again (with parent data)
    try:
        parent = Node.find(node.parent, {
            'projection': '{"name": 1, "parent": 1, "node_type": 1}',
            'embedded': '{"node_type":1}',
            }, api=api)
    except ResourceNotFound:
        parent = None
    except ForbiddenAccess:
        parent = None
    while (parent):
        open_nodes.append(jstree_parse_node(parent))
        # If we have a parent
        if parent.parent:
            try:
                parent = Node.find(parent.parent, {
                    'projection': '{"name":1, "parent":1, "node_type": 1}',
                    'embedded': '{"node_type":1}',
                    }, api=api)
            except ResourceNotFound:
                parent = None
        else:
            parent = None
    open_nodes.reverse()
    #open_nodes.pop(0)

    nodes_list = []

    for node in jstree_get_children(open_nodes[0]['id']):
        # Nodes at the root of the project
        node_dict = {
            'id': node['id'],
            'text': node['text'],
            'type': node['type'],
            'children': True
        }
        if len(open_nodes) > 1:
            # Opening and selecting the tree nodes according to the landing place
            if node['id'] == open_nodes[1]['id']:
                current_dict = node_dict
                current_dict['state'] = {'opened': True}
                current_dict['children'] = jstree_get_children(node['id'])
                # Iterate on open_nodes until the end
                for n in open_nodes[2:]:
                    for c in current_dict['children']:
                        if n['id'] == c['id']:
                            current_dict = c
                            break
                    current_dict['state'] = {'opened': True}
                    current_dict['children'] = jstree_get_children(n['id'])

                # if landing_asset_id:
                #     current_dict['children'] = aux_product_tree_node(open_nodes[-1])
                #     for asset in current_dict['children']:
                #         if int(asset['id'][1:])==landing_asset_id:
                #             asset.update(state=dict(selected=True))

        nodes_list.append(node_dict)
    return nodes_list
Ejemplo n.º 48
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
Ejemplo n.º 49
0
def edit(node_id):
    """Generic node editing form
    """
    def set_properties(
            dyn_schema, form_schema, node_properties, form, prefix="", set_data=True):
        """Initialize custom properties for the form. We run this function once
        before validating the function with set_data=False, so that we can set
        any multiselect field that was originally specified empty and fill it
        with the current choices.
        """
        for prop in dyn_schema:
            if not prop in node_properties:
                continue
            schema_prop = dyn_schema[prop]
            form_prop = form_schema[prop]
            prop_name = "{0}{1}".format(prefix, prop)
            if schema_prop['type'] == 'dict':
                set_properties(
                    schema_prop['schema'],
                    form_prop['schema'],
                    node_properties[prop_name],
                    form,
                    "{0}__".format(prop_name))
            else:
                try:
                    data = node_properties[prop]
                except KeyError:
                    print ("{0} not found in form".format(prop_name))
                if schema_prop['type'] == 'datetime':
                    data = datetime.strptime(data, RFC1123_DATE_FORMAT)
                if prop_name in form:
                    # Other field types
                    if isinstance(form[prop_name], SelectMultipleField):
                        # If we are dealing with a multiselect field, check if
                        # it's empty (usually because we can't query the whole
                        # database to pick all the choices). If it's empty we
                        # populate the choices with the actual data.
                        if not form[prop_name].choices:
                            form[prop_name].choices = [(d,d) for d in data]
                            # Choices should be a tuple with value and name
                    # Assign data to the field
                    if set_data:
                        form[prop_name].data = data

    api = SystemUtility.attract_api()
    node = Node.find(node_id, api=api)
    # TODO: simply embed node_type
    node_type = NodeType.find(node.node_type, api=api)
    form = get_node_form(node_type)
    user_id = current_user.objectid
    dyn_schema = node_type['dyn_schema'].to_dict()
    form_schema = node_type['form_schema'].to_dict()
    error = ""
    node_type_name = node_type.name

    node_properties = node.properties.to_dict()

    set_properties(dyn_schema, form_schema, node_properties, form, set_data=False)

    if form.validate_on_submit():
        if process_node_form(
                form, node_id=node_id, node_type=node_type, user=user_id):
            project_update_nodes_list(node_id)
            return redirect(url_for('nodes.view', node_id=node_id, embed=1))
        else:
            error = "Server error"
            print ("Error sending data")
    else:
        print form.errors

    # Populate Form
    form.name.data = node.name
    form.description.data = node.description
    if 'picture' in form:
        form.picture.data = node.picture
    if node.parent:
        form.parent.data = node.parent

    set_properties(dyn_schema, form_schema, node_properties, form)

    # Get Parent
    try:
        parent = Node.find(node['parent'], api=api)
    except KeyError:
        parent = None
    except ResourceNotFound:
        parent = None

    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'

    template = '{0}/edit{1}.html'.format(node_type['name'], embed_string)

    # We should more simply check if the template file actually exsists on
    # the filesystem level
    try:
        return render_template(
                template,
                node=node,
                parent=parent,
                form=form,
                errors=form.errors,
                error=error)
    except TemplateNotFound:
        template = 'nodes/edit{1}.html'.format(node_type['name'], embed_string)
        return render_template(
                template,
                node=node,
                parent=parent,
                form=form,
                errors=form.errors,
                error=error)
Ejemplo n.º 50
0
def comments_rate(comment_id):
    """Comment rating function

    :param comment_id: the comment id
    :type comment_id: str
    :param rating: the rating (is cast from 0 to False and from 1 to True)
    :type rating: int

    """
    rating_is_positive = False if request.form['is_positive'] == 'false' else True

    api = SystemUtility.attract_api()
    comment = Node.find(comment_id, api=api)

    # Check if comment has been rated
    user_comment_rating = None
    if comment.properties.ratings:
        for rating in comment.properties.ratings:
            if rating['user'] == current_user.objectid:
                user_comment_rating = rating
    #r = next((r for r in comment.ratings if r['user'] == current_user.objectid), None)

    if user_comment_rating:
        # Update or remove rating
        if user_comment_rating['is_positive'] == rating_is_positive:
            # If the rating matches, remove the it
            comment.properties.ratings.remove(user_comment_rating)
            # Update global rating values
            if rating_is_positive:
                comment.properties.rating_positive -= 1
            else:
                comment.properties.rating_negative -= 1
            comment.update(api=api)
            return_data = dict(is_rated=False,
                                rating_up=comment.properties.rating_positive)
        else:
            # If the rating differs from the current, update its value. In this
            # case we make sure we update the existing global rating values as well
            user_comment_rating['is_positive'] = rating_is_positive
            if rating_is_positive:
                comment.properties.rating_positive += 1
                comment.properties.rating_negative -= 1
            else:
                comment.properties.rating_negative += 1
                comment.properties.rating_positive -= 1
            comment.update(api=api)
            return_data = dict(is_positive=rating_is_positive, is_rated=True,
                                rating_up=comment.properties.rating_positive)
    else:
        # Create rating for current user
        user_comment_rating = dict(user=current_user.objectid,
                                    is_positive=rating_is_positive,
                                    # Hardcoded to default (auto valid)
                                    weight=3)
        if not comment.properties.ratings:
            comment.properties.ratings = []
        comment.properties.ratings.append(user_comment_rating)
        if rating_is_positive:
            comment.properties.rating_positive += 1
        else:
            comment.properties.rating_negative += 1
        comment.update(api=api)
        return_data = dict(is_positive=rating_is_positive, is_rated=True,
                            rating_up=comment.properties.rating_positive)

    return jsonify(status='success', data=return_data)
Ejemplo n.º 51
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())
Ejemplo n.º 52
0
def url_for_node(node_id=None, node=None):
    assert isinstance(node_id, (basestring, type(None)))
    # assert isinstance(node, (Node, type(None))), 'wrong type for node: %r' % type(node)

    api = system_util.pillar_api()

    # Find node by its ID, or the ID by the node, depending on what was passed as parameters.
    if node is None:
        try:
            node = Node.find(node_id, api=api)
        except ResourceNotFound:
            log.warning(
                'url_for_node(node_id=%r, node=None): Unable to find node.',
                node_id)
            raise ValueError('Unable to find node %r' % node_id)
    elif node_id is None:
        node_id = node['_id']
    else:
        raise ValueError('Either node or node_id must be given')

    # Find the node's project, or its ID, depending on whether a project was embedded.
    # This is needed in two of the three finder functions.
    project_id = node.project
    if isinstance(project_id, pillarsdk.Resource):
        # Embedded project
        project = project_id
        project_id = project['_id']
    else:
        project = None

    def project_or_error():
        """Returns the project, raising a ValueError if it can't be found."""

        if project is not None:
            return project

        try:
            return Project.find(project_id, {'projection': {
                'url': 1
            }},
                                api=api)
        except ResourceNotFound:
            log.warning('url_for_node(node_id=%r): Unable to find project %r',
                        node_id, project_id)
            raise ValueError('Unable to find node project %r' % project_id)

    def find_for_comment():
        """Returns the URL for a comment."""

        parent = node
        while parent.node_type == 'comment':
            if isinstance(parent.parent, pillarsdk.Resource):
                parent = parent.parent
                continue

            try:
                parent = Node.find(parent.parent, api=api)
            except ResourceNotFound:
                log.warning(
                    'url_for_node(node_id=%r): Unable to find parent node %r',
                    node_id, parent.parent)
                raise ValueError('Unable to find parent node %r' %
                                 parent.parent)

        # Find the redirection URL for the parent node.
        parent_url = url_for_node(node=parent)
        if '#' in parent_url:
            # We can't attach yet another fragment, so just don't link to the comment for now.
            return parent_url
        return parent_url + '#{}'.format(node_id)

    def find_for_post():
        """Returns the URL for a blog post."""

        if str(project_id) == app.config['MAIN_PROJECT_ID']:
            return url_for('main_blog', url=node.properties.url)

        return url_for('project_blog',
                       project_url=project_or_error().url,
                       url=node.properties.url)

    # Fallback: Assets, textures, and other node types.
    def find_for_other():
        return url_for(
            'projects.view',
            project_url=project_or_error().url) + '#{}'.format(node_id)

    # Determine which function to use to find the correct URL.
    url_finders = {
        'comment': find_for_comment,
        'post': find_for_post,
    }

    finder = url_finders.get(node.node_type, find_for_other)
    return finder()