Example #1
0
    def render_page():
        api = system_util.pillar_api()
        projects = Project.all(
            {
                'where': {
                    'category': 'film',
                    'is_private': False
                },
                'sort': '-_created',
            },
            api=api)
        for project in projects._items:
            # Attach poster file (ensure the extension_props.cloud.poster
            # attributes exists)
            try:
                # If the attribute exists, but is None, continue
                if not project['extension_props'][EXTENSION_NAME]['poster']:
                    continue
                # Fetch the file and embed it in the document
                project.extension_props.cloud.poster = get_file(
                    project.extension_props.cloud.poster, api=api)
                # Add convenience attribute that specifies the presence of the
                # poster file
                project.has_poster = True
            # If there was a key error because one of the nested attributes is
            # missing,
            except KeyError:
                continue

        return render_template('films.html',
                               title='films',
                               projects=projects._items,
                               api=system_util.pillar_api())
Example #2
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)
Example #3
0
def save_film_settings(project: pillarsdk.Project):
    # Ensure that the project is setup for Cloud (see @attract_project_view for example)
    form = FilmProjectForm()
    if not form.validate_on_submit():
        log.debug('Form submission failed')
        # Return list of validation errors

    updated_extension_props = {}
    for field_name in form.data:
        # Skip csrf_token field
        if field_name == 'csrf_token':
            continue
        form_field = getattr(form, field_name)
        # TODO(fsiddi) if form_field type is FileSelectField, convert it to ObjectId
        # Currently this raises TypeError: Object of type 'ObjectId' is not JSON serializable

        if form_field.data == '':
            form_field.data = None
        updated_extension_props[field_name] = form_field.data

    # Update extension props and save project
    extension_props = project['extension_props'][EXTENSION_NAME]
    # Project is a Resource, so we update properties iteratively
    for k, v in updated_extension_props.items():
        extension_props[k] = v
    project.update(api=system_util.pillar_api())
    return '', 204
Example #4
0
def emails():
    """Main email settings.
    """
    if current_user.has_role('protected'):
        return abort(404)  # TODO: make this 403, handle template properly
    api = system_util.pillar_api()
    user = User.find(current_user.objectid, api=api)

    # Force creation of settings for the user (safely remove this code once
    # implemented on account creation level, and after adding settings to all
    # existing users)
    if not user.settings:
        user.settings = dict(email_communications=1)
        user.update(api=api)

    if user.settings.email_communications is None:
        user.settings.email_communications = 1
        user.update(api=api)

    # Generate form
    form = forms.UserSettingsEmailsForm(
        email_communications=user.settings.email_communications)

    if form.validate_on_submit():
        try:
            user.settings.email_communications = form.email_communications.data
            user.update(api=api)
            flash("Profile updated", 'success')
        except sdk_exceptions.ResourceInvalid as e:
            message = json.loads(e.content)
            flash(message)

    return render_template('users/settings/emails.html',
                           form=form,
                           title='emails')
Example #5
0
def comments_rate(comment_id, operation):
    """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

    """

    if operation not in {'revoke', 'upvote', 'downvote'}:
        raise wz_exceptions.BadRequest('Invalid operation')

    api = system_util.pillar_api()

    # PATCH the node and return the result.
    comment = Node({'_id': comment_id})
    result = comment.patch({'op': operation}, api=api)
    assert result['_status'] == 'OK'

    return jsonify({
        'status': 'success',
        'data': {
            'op': operation,
            'rating_positive': result.properties.rating_positive,
            'rating_negative': result.properties.rating_negative,
        }
    })
Example #6
0
def index():
    """Get notifications for the current user.

    Optional url args:
    - limit: limits the number of notifications
    """
    limit = request.args.get('limit', 25)
    api = system_util.pillar_api()
    user_notifications = Notification.all(
        {
            'where': {
                'user': str(current_user.objectid)
            },
            'sort': '-_created',
            'max_results': str(limit),
            'parse': '1'
        },
        api=api)
    # TODO: properly investigate and handle missing actors
    items = [
        notification_parse(n) for n in user_notifications['_items']
        if notification_parse(n)
    ]

    return jsonify(items=items)
Example #7
0
def homepage():
    if current_user.is_anonymous:
        return redirect(url_for('cloud.welcome'))

    return render_template(
        'homepage.html',
        api=system_util.pillar_api(),
        **_homepage_context(),
    )
Example #8
0
def project_browse(project: pillarsdk.Project):
    """Project view displaying all top-level nodes.

    We render a regular project view, but we introduce an additional template
    variable: browse. By doing that we prevent the regular project view
    from loading and fetch via AJAX a "group" node-like view instead (see
    project_browse_view_nodes).
    """
    return render_template(
        'projects/view.html',
        api=system_util.pillar_api(),
        project=project,
        node=None,
        show_project=True,
        browse=True,
        og_picture=None,
        navigation_links=project_navigation_links(project,
                                                  system_util.pillar_api()),
        extension_sidebar_links=current_app.extension_sidebar_links(project))
Example #9
0
def _homepage_context() -> dict:
    """Returns homepage template context variables."""

    # Get latest blog posts
    api = system_util.pillar_api()

    # Get latest comments to any node
    latest_comments = Node.latest('comments', api=api)

    # Get a list of random featured assets
    random_featured = get_random_featured_nodes()

    # Parse results for replies
    to_remove = []

    @functools.lru_cache()
    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)

    for idx, comment in enumerate(latest_comments._items):
        if comment.properties.is_reply:
            try:
                comment.attached_to = _find_parent(comment.parent.parent)
            except ResourceNotFound:
                # Remove this comment
                to_remove.append(idx)
        else:
            comment.attached_to = comment.parent

    for idx in reversed(to_remove):
        del latest_comments._items[idx]

    for comment in latest_comments._items:
        if not comment.attached_to:
            continue
        comment.attached_to.url = url_for_node(node=comment.attached_to)
        comment.url = url_for_node(node=comment)

    main_project = Project.find(current_app.config['MAIN_PROJECT_ID'], api=api)
    main_project.picture_header = get_file(main_project.picture_header,
                                           api=api)

    return dict(main_project=main_project,
                latest_comments=latest_comments._items,
                random_featured=random_featured)
Example #10
0
def action_read_toggle(notification_id):
    api = system_util.pillar_api()
    notification = Notification.find(notification_id, api=api)
    if notification.user == current_user.objectid:
        notification.is_read = not notification.is_read
        notification.update(api=api)
        return jsonify(status='success',
                       data=dict(message="Notification {0} is_read {1}".format(
                           notification_id, notification.is_read),
                                 is_read=notification.is_read))
    else:
        return abort(403)
Example #11
0
    def render_page():
        feed = AtomFeed('Blender Cloud - Latest updates',
                        feed_url=ensure_schema(request.url),
                        url=ensure_schema(request.url_root))
        # Get latest blog posts
        api = system_util.pillar_api()
        latest_posts = Node.all(
            {
                'where': {
                    'node_type': 'post',
                    'properties.status': 'published'
                },
                'embedded': {
                    'user': 1
                },
                'sort': '-_created',
                'max_results': '15'
            },
            api=api)

        newest = None

        # Populate the feed
        for post in latest_posts._items:
            author = post.user.fullname or post.user.username
            updated = post._updated if post._updated else post._created
            url = ensure_schema(
                urllib.parse.urljoin(request.host_url,
                                     url_for_node(node=post)))
            content = post.properties.content[:500]
            content = '<p>{0}... <a href="{1}">Read more</a></p>'.format(
                content, url)

            if newest is None:
                newest = updated
            else:
                newest = max(newest, updated)

            feed.add(post.name,
                     str(content),
                     content_type='html',
                     author=author,
                     url=url,
                     updated=updated,
                     published=post._created)
        resp = feed.get_response()
        if newest is not None:
            resp.headers['Last-Modified'] = newest.strftime(
                current_app.config['RFC1123_DATE_FORMAT'])
        return resp
Example #12
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)
Example #13
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)
Example #14
0
def comment_edit(comment_id):
    """Allows a user to edit their comment."""
    from pillar.web import jinja

    api = system_util.pillar_api()

    comment = Node({'_id': comment_id})
    result = comment.patch({'op': 'edit', 'content': request.form['content']}, api=api)
    assert result['_status'] == 'OK'

    return jsonify({
        'status': 'success',
        'data': {
            'content': result.properties.content or '',
            'content_html': jinja.do_markdowned(result.properties, 'content'),
        }})
Example #15
0
def action_subscription_toggle(notification_id):
    """Given a notification id, get the ActivitySubscription and update it by
    toggling the notifications status for the web key.
    """
    api = system_util.pillar_api()
    # Get the notification
    notification = notification_parse(
        Notification.find(notification_id, {'parse': '1'}, api=api))
    # Get the subscription and modify it
    subscription = ActivitySubscription.find(notification['subscription'],
                                             api=api)
    subscription.notifications['web'] = not subscription.notifications['web']
    subscription.update(api=api)
    return jsonify(status='success',
                   data=dict(message="You have been {}subscribed".format(
                       '' if subscription.notifications['web'] else 'un')))
Example #16
0
def project_browse_view_nodes(project: pillarsdk.Project):
    """Display top-level nodes for a Project.

    This view is always meant to be served embedded, as part of project_browse.
    """
    api = system_util.pillar_api()
    # Get top level nodes
    projection = {
        'project': 1,
        'name': 1,
        'picture': 1,
        'node_type': 1,
        'properties.order': 1,
        'properties.status': 1,
        'user': 1,
        'properties.content_type': 1,
        'permissions.world': 1
    }
    where = {
        'project': project['_id'],
        'parent': {
            '$exists': False
        },
        'properties.status': 'published',
        '_deleted': {
            '$ne': True
        },
        'node_type': {
            '$in': ['group', 'asset']
        },
    }

    try:
        nodes = Node.all(
            {
                'projection': projection,
                'where': where,
                'sort': [('properties.order', 1), ('name', 1)]
            },
            api=api)
    except pillarsdk.exceptions.ForbiddenAccess:
        return render_template('errors/403_embed.html')
    nodes = nodes._items

    for child in nodes:
        child.picture = get_file(child.picture, api=api)
    return render_template('projects/browse_embed.html', nodes=nodes)
Example #17
0
def get_projects(category):
    """Utility to get projects based on category. Should be moved on the API
    and improved with more extensive filtering capabilities.
    """
    api = system_util.pillar_api()
    projects = Project.all(
        {
            'where': {
                'category': category,
                'is_private': False
            },
            'sort': '-_created',
        },
        api=api)
    for project in projects._items:
        attach_project_pictures(project, api)
    return projects
Example #18
0
def action_read_all():
    """Mark all notifications as read"""
    api = system_util.pillar_api()
    notifications = Notification.all(
        {
            'where': '{"user": "******"}' % current_user.objectid,
            'sort': '-_created'
        },
        api=api)

    for notification in notifications._items:
        notification = Notification.find(notification._id, api=api)
        notification.is_read = True
        notification.update(api=api)

    return jsonify(status='success',
                   data=dict(message="All notifications mark as read"))
Example #19
0
def posts_create(project_id):
    api = system_util.pillar_api()
    try:
        project = Project.find(project_id, api=api)
    except ResourceNotFound:
        return abort(404)
    attach_project_pictures(project, api)

    blog = Node.find_one(
        {'where': {
            'node_type': 'blog',
            'project': project_id
        }}, api=api)
    node_type = project.get_node_type('post')
    # Check if user is allowed to create a post in the blog
    if not project.node_type_has_method('post', 'POST', api=api):
        return abort(403)
    form = get_node_form(node_type)
    if form.validate_on_submit():
        # Create new post object from scratch
        post_props = dict(node_type='post',
                          name=form.name.data,
                          picture=form.picture.data,
                          user=current_user.objectid,
                          parent=blog._id,
                          project=project._id,
                          properties=dict(content=form.content.data,
                                          status=form.status.data,
                                          url=form.url.data))
        if form.picture.data == '':
            post_props['picture'] = None
        post = Node(post_props)
        post.create(api=api)
        # Only if the node is set as published, push it to the list
        if post.properties.status == 'published':
            project_update_nodes_list(post,
                                      project_id=project._id,
                                      list_name='blog')
        return redirect(url_for_node(node=post))
    form.parent.data = blog._id
    return render_template('nodes/custom/post/create.html',
                           node_type=node_type,
                           form=form,
                           project=project,
                           api=api)
Example #20
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
Example #21
0
    def test_edit_with_explicit_owner(self):
        """Test editing a node as the owner of the node."""
        import pillarsdk
        from pillar.web.utils import system_util

        other_user_id = self.create_user(user_id='cafef005972666988bef6500',
                                         groups=[self.dillo_user_main_grp])
        self.create_valid_auth_token(other_user_id, 'other_token')
        test_node = copy.deepcopy(self.test_node)
        node_doc = self._test_user(test_node, auth_token='other_token')
        # Is PUT allowed?
        self.assertIn('PUT', node_doc['allowed_methods'])

        with self.app.test_request_context():
            api = system_util.pillar_api(token='other_token')
            node = pillarsdk.Node.find(node_doc['_id'], api=api)
            node.properties.content = 'Some content here'
            node.properties.status = 'published'
            node.update(api=api)
Example #22
0
def render_comments_for_node(node_id: str, *, can_post_comments: bool):
    """Render the list of comments for a node."""
    api = system_util.pillar_api()

    # 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)
Example #23
0
    def render_page():
        feed = AtomFeed('Blender Cloud - Latest updates',
                        feed_url=request.url,
                        url=request.url_root)
        # Get latest blog posts
        api = system_util.pillar_api()
        latest_posts = Node.all(
            {
                'where': {
                    'node_type': 'post',
                    'properties.status': 'published'
                },
                'embedded': {
                    'user': 1
                },
                'sort': '-_created',
                'max_results': '15'
            },
            api=api)

        # Populate the feed
        for post in latest_posts._items:
            author = post.user.fullname
            updated = post._updated if post._updated else post._created
            url = url_for_node(node=post)
            content = post.properties.content[:500]
            content = '<p>{0}... <a href="{1}">Read more</a></p>'.format(
                content, url)
            feed.add(post.name,
                     str(content),
                     content_type='html',
                     author=author,
                     url=url,
                     updated=updated,
                     published=post._created)
        return feed.get_response()
Example #24
0
 def render_page():
     projects = get_projects('workshop')
     return render_template('projects_index_collection.html',
                            title='workshops',
                            projects=projects._items,
                            api=system_util.pillar_api())
Example #25
0
def get_random_featured_nodes() -> typing.List[dict]:
    """Returns a list of project/node combinations for featured nodes.

    A random subset of 3 featured nodes from all public projects is returned.
    Assumes that the user actually has access to the public projects' nodes.

    The dict is a node, with a 'project' key that contains a projected project.
    """

    proj_coll = current_app.db('projects')
    featured_nodes = proj_coll.aggregate([
        {
            '$match': {
                'is_private': False
            }
        },
        {
            '$project': {
                'nodes_featured': True,
                'url': True,
                'name': True,
                'summary': True,
                'picture_square': True
            }
        },
        {
            '$unwind': {
                'path': '$nodes_featured'
            }
        },
        {
            '$sample': {
                'size': 6
            }
        },
        {
            '$lookup': {
                'from': 'nodes',
                'localField': 'nodes_featured',
                'foreignField': '_id',
                'as': 'node'
            }
        },
        {
            '$unwind': {
                'path': '$node'
            }
        },
        {
            '$match': {
                'node._deleted': {
                    '$ne': True
                }
            }
        },
        {
            '$project': {
                'url': True,
                'name': True,
                'summary': True,
                'picture_square': True,
                'node._id': True,
                'node.name': True,
                'node.permissions': True,
                'node.picture': True,
                'node.properties.content_type': True,
                'node.properties.duration_seconds': True,
                'node.properties.url': True,
                'node._created': True,
            }
        },
    ])

    featured_node_documents = []
    api = system_util.pillar_api()
    for node_info in featured_nodes:
        # Turn the project-with-node doc into a node-with-project doc.
        node_document = node_info.pop('node')
        node_document['project'] = node_info
        node_document['_id'] = str(node_document['_id'])

        node = Node(node_document)
        node.picture = get_file(node.picture, api=api)
        node.project.picture_square = get_file(node.project.picture_square,
                                               api=api)
        featured_node_documents.append(node)

    return featured_node_documents
Example #26
0
def process_node_form(form, node_id=None, node_type=None, user=None):
    """Generic function used to process new nodes, as well as edits
    """
    if not user:
        log.warning(
            'process_node_form(node_id=%s) called while user not logged in',
            node_id)
        return False

    api = system_util.pillar_api()
    node_schema = node_type['dyn_schema'].to_dict()
    form_schema = node_type['form_schema'].to_dict()

    if node_id:
        # Update existing node
        node = pillarsdk.Node.find(node_id, api=api)
        node.name = form.name.data
        node.description = form.description.data
        if 'picture' in form:
            node.picture = form.picture.data
            if node.picture == 'None' or node.picture == '':
                node.picture = None
        if 'parent' in form:
            if form.parent.data != "":
                node.parent = form.parent.data

        for prop_name, schema_prop, form_prop in iter_node_properties(
                node_type):
            data = form[prop_name].data
            if schema_prop['type'] == 'dict':
                data = attachments.attachment_form_parse_post_data(data)
            elif schema_prop['type'] == 'integer':
                if not data:
                    data = None
                else:
                    data = int(form[prop_name].data)
            elif schema_prop['type'] == 'float':
                if not data:
                    data = None
                else:
                    data = float(form[prop_name].data)
            elif schema_prop['type'] == 'datetime':
                data = datetime.strftime(
                    data, current_app.config['RFC1123_DATE_FORMAT'])
            elif schema_prop['type'] == 'list':
                if prop_name == 'files':
                    # Only keep those items that actually refer to a file.
                    data = [
                        file_item for file_item in data
                        if file_item.get('file')
                    ]
                else:
                    log.warning('Ignoring property %s of type %s', prop_name,
                                schema_prop['type'])
                # elif pr == 'tags':
                #     data = [tag.strip() for tag in data.split(',')]
            elif schema_prop['type'] == 'objectid':
                if data == '':
                    # Set empty object to None so it gets removed by the
                    # SDK before node.update()
                    data = None
            else:
                if prop_name in form:
                    data = form[prop_name].data
            path = prop_name.split('__')
            assert len(path) == 1
            if len(path) > 1:
                recursive_prop = recursive(path, node.properties.to_dict(),
                                           data)
                node.properties = recursive_prop
            else:
                node.properties[prop_name] = data

        ok = node.update(api=api)
        if not ok:
            log.warning('Unable to update node: %s', node.error)
        # if form.picture.data:
        #     image_data = request.files[form.picture.name].read()
        #     post = node.replace_picture(image_data, api=api)
        return ok
    else:
        # Create a new node
        node = pillarsdk.Node()
        prop = {}
        files = {}
        prop['name'] = form.name.data
        prop['description'] = form.description.data
        prop['user'] = user
        if 'picture' in form:
            prop['picture'] = form.picture.data
            if prop['picture'] == 'None' or prop['picture'] == '':
                prop['picture'] = None
        if 'parent' in form:
            prop['parent'] = form.parent.data
        prop['properties'] = {}

        def get_data(node_schema, form_schema, prefix=""):
            for pr in node_schema:
                schema_prop = node_schema[pr]
                form_prop = form_schema.get(pr, {})
                if pr == 'items':
                    continue
                if 'visible' in form_prop and not form_prop['visible']:
                    continue
                prop_name = "{0}{1}".format(prefix, pr)
                if schema_prop['type'] == 'dict':
                    get_data(schema_prop['schema'], form_prop['schema'],
                             "{0}__".format(prop_name))
                    continue
                data = form[prop_name].data
                if schema_prop['type'] == 'media':
                    tmpfile = '/tmp/binary_data'
                    data.save(tmpfile)
                    binfile = open(tmpfile, 'rb')
                    files[pr] = binfile
                    continue
                if schema_prop['type'] == 'integer':
                    if data == '':
                        data = 0
                if schema_prop['type'] == 'list':
                    if data == '':
                        data = []
                if schema_prop['type'] == 'datetime':
                    data = datetime.strftime(data,
                                             app.config['RFC1123_DATE_FORMAT'])
                if schema_prop['type'] == 'objectid':
                    if data == '':
                        data = None
                path = prop_name.split('__')
                if len(path) > 1:
                    prop['properties'] = recursive(path, prop['properties'],
                                                   data)
                else:
                    prop['properties'][prop_name] = data

        get_data(node_schema, form_schema)

        prop['node_type'] = form.node_type_id.data
        post = node.post(prop, api=api)

        return post
Example #27
0
def posts_view(project_id=None, project_url=None, url=None):
    """View individual blogpost"""

    if bool(project_id) == bool(project_url):
        raise ValueError('posts_view(): pass either project_id or project_url')

    api = system_util.pillar_api()

    # Fetch project (for backgroud images and links generation)
    if project_id:
        project = Project.find(project_id, api=api)
    else:
        project = Project.find_one({'where': {'url': project_url}}, api=api)
        project_id = project['_id']

    attach_project_pictures(project, api)

    blog = Node.find_one(
        {
            'where': {
                'node_type': 'blog',
                'project': project_id
            },
        }, api=api)

    status_query = "" if blog.has_method(
        'PUT') else ', "properties.status": "published"'
    posts = Node.all(
        {
            'where': '{"parent": "%s" %s}' % (blog._id, status_query),
            'embedded': '{"user": 1}',
            'sort': '-_created'
        },
        api=api)

    for post in posts._items:
        post.picture = get_file(post.picture, api=api)

        post['properties'][
            'content'] = pillar.web.nodes.attachments.render_attachments(
                post, post['properties']['content'])

    # Use the *_main_project.html template for the main blog
    main_project_template = '_main_project' if project_id == current_app.config[
        'MAIN_PROJECT_ID'] else ''

    if url:
        post = Node.find_one(
            {
                'where': {
                    'parent': blog._id,
                    'properties.url': url
                },
                'embedded': {
                    'node_type': 1,
                    'user': 1
                },
            },
            api=api)
        if post.picture:
            post.picture = get_file(post.picture, api=api)

        # If post is not published, check that the user is also the author of
        # the post. If not, return 404.
        if post.properties.status != "published":
            if not (current_user.is_authenticated and post.has_method('PUT')):
                abort(403)

        post['properties'][
            'content'] = pillar.web.nodes.attachments.render_attachments(
                post, post['properties']['content'])
        return render_template(
            'nodes/custom/post/view{0}.html'.format(main_project_template),
            blog=blog,
            node=post,
            posts=posts._items,
            project=project,
            title='blog',
            api=api)
    else:
        node_type_post = project.get_node_type('post')
        template_path = 'nodes/custom/blog/index.html'

        return render_template(
            'nodes/custom/blog/index{0}.html'.format(main_project_template),
            node_type_post=node_type_post,
            posts=posts._items,
            project=project,
            title='blog',
            api=api)
Example #28
0
def project_landing(project_url):
    """Override Pillar project_view endpoint completely.

    The first part of the function is identical to the one in Pillar, but the
    second part (starting with 'Load custom project properties') extends the
    behaviour to support film project landing pages.
    """

    template_name = None
    if request.args.get('format') == 'jstree':
        log.warning(
            'projects.view(%r) endpoint called with format=jstree, '
            'redirecting to proper endpoint. URL is %s; referrer is %s',
            project_url, request.url, request.referrer)
        return redirect(url_for('projects.jstree', project_url=project_url))

    api = system_util.pillar_api()
    project = find_project_or_404(project_url,
                                  embedded={'header_node': 1},
                                  api=api)

    # Load the header video file, if there is any.
    header_video_file = None
    header_video_node = None
    if project.header_node and project.header_node.node_type == 'asset' and \
            project.header_node.properties.content_type == 'video':
        header_video_node = project.header_node
        header_video_file = get_file(project.header_node.properties.file)
        header_video_node.picture = get_file(header_video_node.picture)

    extra_context = {
        'header_video_file': header_video_file,
        'header_video_node': header_video_node
    }

    # Load custom project properties. If the project has a 'cloud' extension prop,
    # render it using the projects/landing.html template and try to attach a
    # number of additional attributes (pages, images, etc.).
    if 'extension_props' in project and EXTENSION_NAME in project[
            'extension_props']:
        extension_props = project['extension_props'][EXTENSION_NAME]
        extension_props['logo'] = get_file(extension_props['logo'])

        pages = Node.all(
            {
                'where': {
                    'project': project._id,
                    'node_type': 'page',
                    '_deleted': {
                        '$ne': True
                    }
                },
                'projection': {
                    'name': 1
                }
            },
            api=api)

        extra_context.update({'pages': pages._items})
        template_name = 'projects/landing.html'

    return render_project(project,
                          api,
                          extra_context=extra_context,
                          template_name=template_name)
Example #29
0
def posts_view(project_id=None,
               project_url=None,
               url=None,
               *,
               archive=False,
               page=1):
    """View individual blogpost"""

    if bool(project_id) == bool(project_url):
        raise ValueError('posts_view(): pass either project_id or project_url')

    if url and archive:
        raise ValueError('posts_view(): cannot pass both url and archive')

    api = system_util.pillar_api()

    # Fetch project (for background images and links generation)
    if project_id:
        project = Project.find(project_id, api=api)
    else:
        project = Project.find_one({'where': {'url': project_url}}, api=api)
        project_id = project['_id']

    attach_project_pictures(project, api)

    blog = Node.find_one(
        {
            'where': {
                'node_type': 'blog',
                'project': project_id
            },
        }, api=api)

    status_query = {} if blog.has_method('PUT') else {
        'properties.status': 'published'
    }
    posts = Node.all(
        {
            'where': {
                'parent': blog._id,
                **status_query
            },
            'embedded': {
                'user': 1
            },
            'sort': '-_created',
            'max_results': 20 if archive else 5,
            'page': page,
        },
        api=api)

    for post in posts._items:
        post.picture = get_file(post.picture, api=api)
        post.url = url_for_node(node=post)

    # Use the *_main_project.html template for the main blog
    is_main_project = project_id == current_app.config['MAIN_PROJECT_ID']
    main_project_template = '_main_project' if is_main_project else ''
    main_project_template = '_main_project'
    index_arch = 'archive' if archive else 'index'
    template_path = f'nodes/custom/blog/{index_arch}{main_project_template}.html',

    if url:
        template_path = f'nodes/custom/post/view{main_project_template}.html',

        post = Node.find_one(
            {
                'where': {
                    'parent': blog._id,
                    'properties.url': url
                },
                'embedded': {
                    'node_type': 1,
                    'user': 1
                },
            },
            api=api)

        if post.picture:
            post.picture = get_file(post.picture, api=api)

        post.url = url_for_node(node=post)
    elif posts._items:
        post = posts._items[0]
    else:
        post = None

    if post is not None:
        # If post is not published, check that the user is also the author of
        # the post. If not, return an error.
        if post.properties.status != "published":
            if not (current_user.is_authenticated and post.has_method('PUT')):
                abort(403)

    can_create_blog_posts = project.node_type_has_method('post',
                                                         'POST',
                                                         api=api)

    # Use functools.partial so we can later pass page=X.
    if is_main_project:
        url_func = functools.partial(url_for, 'main.main_blog_archive')
    else:
        url_func = functools.partial(url_for,
                                     'main.project_blog_archive',
                                     project_url=project.url)

    project.blog_archive_url = url_func()
    pmeta = posts._meta
    seen_now = pmeta['max_results'] * pmeta['page']
    if pmeta['total'] > seen_now:
        project.blog_archive_next = url_func(page=pmeta['page'] + 1)
    else:
        project.blog_archive_next = None
    if pmeta['page'] > 1:
        project.blog_archive_prev = url_func(page=pmeta['page'] - 1)
    else:
        project.blog_archive_prev = None

    title = 'blog_main' if is_main_project else 'blog'

    pages = Node.all(
        {
            'where': {
                'project': project._id,
                'node_type': 'page'
            },
            'projection': {
                'name': 1
            }
        },
        api=api)

    return render_template(
        template_path,
        blog=blog,
        node=post,
        posts=posts._items,
        posts_meta=pmeta,
        more_posts_available=pmeta['total'] > pmeta['max_results'],
        project=project,
        title=title,
        node_type_post=project.get_node_type('post'),
        can_create_blog_posts=can_create_blog_posts,
        pages=pages._items,
        api=api)