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)
def index(community_url): api = system_util.pillar_api() project = Project.find_first({'where': {'url': community_url}}, api=api) if project is None: return abort(404) attach_project_pictures(project, api) # Fetch all activities for the main project activities = Activity.all({ 'where': { 'project': project['_id'], }, 'sort': [('_created', -1)], 'max_results': 15, }, api=api) # Fetch more info for each activity. for act in activities['_items']: act.actor_user = subquery.get_user_info(act.actor_user) try: act.link = url_for_node(node_id=act.object) except ValueError: # If the node was delete, we get ValueError exception. # By setting act.link to '', it does not get displayed in the list. act.link = '' return render_template( 'dillo/index.html', col_right={'activities': activities}, project=project, submit_menu=project_submit_menu(project))
def jstree_parse_node(node, children=None): """Generate JStree node from node object""" from pillar.web.nodes.routes import url_for_node node_type = node.node_type # Define better the node type if node_type == 'asset': if not node.properties or not node.properties.content_type: log.warning( 'Asset node %s has no node.properties.content_type: %s', node._id, node) else: node_type = node.properties.content_type parsed_node = dict(id="n_{0}".format(node._id), a_attr={"href": url_for_node(node=node)}, li_attr={"data-node-type": node.node_type}, custom_view=node_type in CUSTOM_VIEW_NODE_TYPES, text=Markup.escape(node.name), type=node_type, children=False) # Append children property only if it is a directory type if node_type in GROUP_NODES: parsed_node['children'] = True if node.permissions and node.permissions.world: parsed_node['li_attr']['is_free'] = True return parsed_node
def link_for_activity(self, act): """Returns the URL for the activity. :type act: pillarsdk.Activity """ from .node_types.job import node_type_job from .node_types.manager import node_type_manager from .node_types.task import node_type_task if act.node_type == node_type_task['name']: return flask.url_for('flamenco.tasks.perproject.view_task', project_url=act.project.url, task_id=act.object) elif act.node_type == node_type_job['name']: return flask.url_for('flamenco.jobs.perproject.view_job', project_url=act.project.url, job_id=act.object) elif act.node_type == node_type_manager['name']: return flask.url_for('flamenco.managers.perproject.view_manager', project_url=act.project.url, manager_id=act.object) return url_for_node(node_id=act.object)
def do_url_for_node(node_id=None, node=None): try: return url_for_node(node_id=node_id, node=node) except wz_exceptions.NotFound: log.info( '%s: do_url_for_node(node_id=%r, ...) called for non-existing node.', flask.request.url, node_id) return None
def render_page(): feed = AtomFeed('Blender Cloud - Latest updates', feed_url=ensure_schema(request.url), url=ensure_schema(request.url_root)) # Get latest blog posts api = system_util.pillar_api() latest_posts = Node.all( { 'where': { 'node_type': 'post', 'properties.status': 'published' }, 'embedded': { 'user': 1 }, 'sort': '-_created', 'max_results': '15' }, api=api) newest = None # Populate the feed for post in latest_posts._items: author = post.user.fullname or post.user.username updated = post._updated if post._updated else post._created url = ensure_schema( urllib.parse.urljoin(request.host_url, url_for_node(node=post))) content = post.properties.content[:500] content = '<p>{0}... <a href="{1}">Read more</a></p>'.format( content, url) if newest is None: newest = updated else: newest = max(newest, updated) feed.add(post.name, str(content), content_type='html', author=author, url=url, updated=updated, published=post._created) resp = feed.get_response() if newest is not None: resp.headers['Last-Modified'] = newest.strftime( current_app.config['RFC1123_DATE_FORMAT']) return resp
def 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)
def test_random_feature_node_returns_6_nodes(self): from pillar.web.nodes.routes import url_for_node base_node = { 'name': 'Just a node name', 'project': self.pid, 'description': '', 'node_type': 'asset', 'user': self.uid, } base_props = { 'status': 'published', 'file': self.file_id, 'content_type': 'video', 'order': 0 } def create_asset(weeks): return self.create_node({ **base_node, '_created': self.fake_now - timedelta(weeks=weeks), 'properties': base_props, }) all_asset_ids = [create_asset(i) for i in range(20)] with self.app.app_context(): proj_col = self.app.db('projects') proj_col.update_one({'_id': self.pid}, {'$set': { 'nodes_featured': all_asset_ids, }}) with self.app.test_request_context(): random_assets = get_random_featured_nodes() self.assertIs(len(random_assets), 6) for asset in random_assets: aid = asset['_id'] self.assertIn(ObjectId(asset['_id']), all_asset_ids) self.assertEqual(f'/p/default-project/{aid}', url_for_node(node=asset))
def redirect_with_short_code(short_code): if any(c not in short_code_chars for c in short_code): # Can't be a short code return log.debug('Path %s may be a short-code', short_code) api = system_util.pillar_api() try: node = pillarsdk.Node.find_one({'where': {'short_code': short_code}, 'projection': {'_id': 1}}, api=api) except pillarsdk.ResourceNotFound: log.debug("Nope, it isn't.") return # Redirect to 'theatre' view for the node. url = url_for_node(node=node) url = urllib.parse.urljoin(url, '?t') log.debug('Found short code %s, redirecting to %s', short_code, url) return redirect(url, code=307)
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()
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)
def users_view(username): """View public user page. Use direct db call to retrieve the user and then use the api to query the paginated list of all published dillo_posts from the user. """ users_coll = current_app.db('users') user = users_coll.find_one({'username': username}, projection={ 'username': 1, 'full_name': 1, 'email': 1, 'extension_props_public': 1, '_updated': 1, '_created': 1, }) if user is None: return abort(404) api = system_util.pillar_api() nodes_coll = current_app.db('nodes') pipeline = [{ '$match': { 'user': ObjectId(user['_id']), 'node_type': 'dillo_post', 'properties.status': 'published', '_deleted': False } }, { '$lookup': { 'from': 'projects', 'localField': 'project', 'foreignField': '_id', 'as': 'project' } }, { '$project': { 'name': 1, 'properties': 1, 'user': 1, 'picture': 1, '_created': 1, 'project': { '$arrayElemAt': ['$project', 0] } } }, { '$sort': { '_created': -1 } }] posts = list(nodes_coll.aggregate(pipeline=pipeline)) for post in posts: if post.get('picture'): post['picture'] = get_file(post['picture'], api=api) main_project_url = current_app.config['DEFAULT_COMMUNITY'] project = Project.find_by_url(main_project_url, api=api) attach_project_pictures(project, api) # Fetch all comments activity for the user activities = Activity.all( { 'where': { 'actor_user': str(user['_id']), 'node_type': 'comment' }, 'sort': [('_created', -1)], 'max_results': 15, }, api=api) # Fetch more info for each activity. for act in activities['_items']: act.actor_user = subquery.get_user_info(act.actor_user) try: act.link = url_for_node(node_id=act.object) except ValueError: # TODO: properly handle the case when the activity object has been deleted continue return render_template('dillo/user.html', col_right={'activities': activities}, user=user, posts=posts, project=project)