def get(self, request, *args, **kwargs): """Show a threadedcomments discussion of an arbitrary object. discussion_id is the pk of the root comment.""" discussion_id = kwargs.get('discussion_id', None) root_comment = get_object_or_404(ThreadedComment, pk=discussion_id) if not root_comment.content_object.permission_to( 'read', request.course, request.user): return HttpResponseForbidden('You do not have permission \ to view this discussion.') try: my_course = root_comment.content_object.context.content_object except: # legacy: for when contexts weren't being set in new() my_course = request.course root_comment.content_object.context = \ Collaboration.objects.get_for_object(my_course) root_comment.content_object.save() data = {'space_owner': request.user.username} if not request.is_ajax(): data['discussion'] = root_comment return render_to_response('discussions/discussion.html', data, context_instance=RequestContext(request)) else: vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.filter(course=request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data['panels'] = [{ 'panel_state': 'open', 'subpanel_state': 'open', 'panel_state_label': "Discussion", 'template': 'discussion', 'owners': owners, 'vocabulary': vocabulary, 'title': root_comment.title, 'can_edit_title': my_course.is_faculty(request.user), 'root_comment_id': root_comment.id, 'context': threaded_comment_json(request, root_comment) }] # Create a place for asset editing panel = {'panel_state': 'closed', 'panel_state_label': "Item Details", 'template': 'asset_quick_edit', 'update_history': False, 'show_collection': False, 'owners': owners, 'vocabulary': vocabulary, 'context': {'type': 'asset'}} data['panels'].append(panel) return HttpResponse(json.dumps(data), content_type='application/json')
def get(self, request, asset_id=None, annot_id=None): if asset_id: try: asset = Asset.objects.get(pk=asset_id, course=request.course) asset.primary except Asset.DoesNotExist: return asset_switch_course(request, asset_id) except Source.DoesNotExist: ctx = RequestContext(request) return render_to_response('500.html', {}, context_instance=ctx) data = {'asset_id': asset_id, 'annotation_id': annot_id} if not request.is_ajax(): return render_to_response('assetmgr/asset_workspace.html', data, context_instance=RequestContext(request)) ctx = {'type': 'asset'} if asset_id: # @todo - refactor this context out of the mix # ideally, the client would simply request the json # the mixin is expecting a queryset, so this becomes awkward here assets = Asset.objects.filter(pk=asset_id) (assets, notes) = self.visible_assets_and_notes(request, assets) # only return original author's global annotations notes = notes.exclude(~Q(author=request.user), range1__isnull=True) ares = AssetResource() ctx.update(ares.render_one_context(request, asset, notes)) help_setting = UserSetting.get_setting(request.user, "help_item_detail_view", True) ctx['user_settings'] = {'help_item_detail_view': help_setting} vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) update_history = True show_collection = True template = 'asset_workspace' data['panels'] = [{ 'panel_state': 'open', 'panel_state_label': "Annotate Media", 'context': ctx, 'owners': owners, 'vocabulary': vocabulary, 'template': template, 'current_asset': asset_id, 'current_annotation': annot_id, 'update_history': update_history, 'show_collection': show_collection}] return self.render_to_json_response(data)
def get(self, request): user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data = { 'owners': json.dumps(owners), 'return_url': request.GET.get('return_url', '') } return self.render_to_response(data)
def get(self, request, asset_id=None, annot_id=None): if asset_id: try: asset = Asset.objects.get(pk=asset_id, course=request.course) asset.primary except Asset.DoesNotExist: return asset_switch_course(request, asset_id) except Source.DoesNotExist: ctx = RequestContext(request) return render_to_response('500.html', {}, context_instance=ctx) data = {'asset_id': asset_id, 'annotation_id': annot_id} if not request.is_ajax(): return render_to_response('assetmgr/asset_workspace.html', data, context_instance=RequestContext(request)) context = {'type': 'asset'} if asset_id: # @todo - refactor this context out of the mix # ideally, the client would simply request the json # the mixin is expecting a queryset, so this becomes awkward here self.record_owner = request.user assets = Asset.objects.filter(pk=asset_id) (assets, notes) = self.visible_assets_and_notes(request, assets) context['assets'] = { asset.pk: AssetResource().render_one(request, asset, notes) } help_setting = UserSetting.get_setting(request.user, "help_item_detail_view", True) context['user_settings'] = {'help_item_detail_view': help_setting} vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data['panels'] = [{ 'panel_state': 'open', 'panel_state_label': "Annotate Media", 'context': context, 'owners': owners, 'vocabulary': vocabulary, 'template': 'asset_workspace', 'current_asset': asset_id, 'current_annotation': annot_id, 'update_history': True, 'show_collection': True }] return self.render_to_json_response(data)
def get(self, request): user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data = { 'owners': json.dumps(owners), 'return_url': request.GET.get('return_url', ''), 'width': EMBED_WIDTH, 'height': EMBED_HEIGHT } return self.render_to_response(data)
def get(self, request): user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data = { 'owners': json.dumps(owners), 'return_url': request.GET.get('return_url', ''), 'width': EMBED_WIDTH, 'height': EMBED_HEIGHT } return self.render_to_response(data)
def asset_workspace(request, asset_id=None, annot_id=None): if not request.user.is_staff: in_course_or_404(request.user.username, request.course) if asset_id: try: asset = Asset.objects.get(pk=asset_id, course=request.course) asset.primary except Asset.DoesNotExist: return asset_switch_course(request, asset_id) except Source.DoesNotExist: response = direct_to_template(request, "500.html", {}) response.status_code = 500 return response data = { 'space_owner': request.user.username, 'asset_id': asset_id, 'annotation_id': annot_id } if not request.is_ajax(): return render_to_response('assetmgr/asset_workspace.html', data, context_instance=RequestContext(request)) elif asset_id: # @todo - refactor this context out of the mix # ideally, the client would simply request the json context = detail_asset_json(request, asset) else: context = {'type': 'asset'} vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data['panels'] = [{ 'panel_state': 'open', 'panel_state_label': "Annotate Media", 'context': context, 'owners': owners, 'vocabulary': vocabulary, 'template': 'asset_workspace', 'current_asset': asset_id, 'current_annotation': annot_id, 'update_history': True, 'show_collection': True, }] return HttpResponse(simplejson.dumps(data, indent=2), mimetype='application/json')
def asset_workspace(request, asset_id=None, annot_id=None): if not request.user.is_staff: in_course_or_404(request.user.username, request.course) if asset_id: try: asset = Asset.objects.get(pk=asset_id, course=request.course) asset.primary except Asset.DoesNotExist: return asset_switch_course(request, asset_id) except Source.DoesNotExist: response = direct_to_template(request, "500.html", {}) response.status_code = 500 return response data = {'space_owner': request.user.username, 'asset_id': asset_id, 'annotation_id': annot_id} if not request.is_ajax(): return render_to_response('assetmgr/asset_workspace.html', data, context_instance=RequestContext(request)) elif asset_id: # @todo - refactor this context out of the mix # ideally, the client would simply request the json context = detail_asset_json(request, asset) else: context = {'type': 'asset'} vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data['panels'] = [{'panel_state': 'open', 'panel_state_label': "Annotate Media", 'context': context, 'owners': owners, 'vocabulary': vocabulary, 'template': 'asset_workspace', 'current_asset': asset_id, 'current_annotation': annot_id, 'update_history': True, 'show_collection': True, }] return HttpResponse(simplejson.dumps(data, indent=2), mimetype='application/json')
def get(self, request, course_pk=None, asset_id=None, annot_id=None): if asset_id: try: asset = Asset.objects.get(pk=asset_id, course=request.course) asset.primary except Asset.DoesNotExist: return asset_switch_course(request, asset_id) except Source.DoesNotExist: return render(request, '500.html', {}) if not request.is_ajax(): return self.redirect_to_react_views(request.course.id, asset_id, annot_id) ctx = {'asset_id': asset_id, 'annotation_id': annot_id} qs = Vocabulary.objects.filter(course=request.course) vocabulary = VocabularyResource().render_list(request, qs) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) ctx['panels'] = [{ 'panel_state': 'open', 'panel_state_label': "Annotate Media", 'context': { 'type': 'asset', 'is_faculty': self.is_viewer_faculty, 'allow_item_download': self.is_viewer_faculty and course_details.allow_item_download(request.course) }, 'owners': owners, 'vocabulary': vocabulary, 'template': 'asset_workspace', 'current_asset': asset_id, 'current_annotation': annot_id, 'update_history': True, 'show_collection': True }] return self.render_to_json_response(ctx)
def get(self, request, asset_id=None, annot_id=None): if asset_id: try: asset = Asset.objects.get(pk=asset_id, course=request.course) asset.primary except Asset.DoesNotExist: return asset_switch_course(request, asset_id) except Source.DoesNotExist: ctx = RequestContext(request) return render_to_response('500.html', {}, context_instance=ctx) ctx = {'asset_id': asset_id, 'annotation_id': annot_id} if not request.is_ajax(): return render_to_response('assetmgr/asset_workspace.html', ctx, context_instance=RequestContext(request)) qs = Vocabulary.objects.filter(course=request.course) vocabulary = VocabularyResource().render_list(request, qs) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) ctx['panels'] = [{ 'panel_state': 'open', 'panel_state_label': "Annotate Media", 'context': { 'type': 'asset', 'is_faculty': self.is_viewer_faculty, 'allow_item_download': self.is_viewer_faculty and allow_item_download(request.course) }, 'owners': owners, 'vocabulary': vocabulary, 'template': 'asset_workspace', 'current_asset': asset_id, 'current_annotation': annot_id, 'update_history': True, 'show_collection': True}] return self.render_to_json_response(ctx)
def discussion_create(request): """Start a discussion of an arbitrary model instance.""" title = request.POST['comment_html'] # Find the object we're discussing. the_content_type = ContentType.objects.get( app_label=request.POST['app_label'], model=request.POST['model']) assert the_content_type is not None the_object = the_content_type.get_object_for_this_type( pk=request.POST['obj_pk']) assert the_object is not None try: obj_sc = Collaboration.objects.get_for_object(the_object) except Collaboration.DoesNotExist: obj_sc = Collaboration() # TODO: populate this collab with sensible auth defaults. obj_sc.content_object = the_object obj_sc.save() # sky: I think what I want to do is have the ThreadedComment # point to the_object # and the collaboration will point to the threaded root comment # that way, whereas, if we live in Collaboration-land, # we can get to ThreadedComments # threaded comments can also live in it's own world without 'knowing' # about SC OTOH, threaded comment shouldn't be able # to point to the regular object # until Collaboration says it's OK (i.e. has permissions) # ISSUE: how to migrate? (see models.py) # now create the CHILD collaboration object for the discussion to point at. # This represents the auth for the discussion itself. disc_sc = Collaboration(_parent=obj_sc, title=title, # or we could point it at the root # threadedcomments object. # content_object=None, context=request.collaboration_context, ) disc_sc.set_policy(request.POST.get('publish', None)) disc_sc.save() # finally create the root discussion object, pointing it at the CHILD. new_threaded_comment = ThreadedComment(parent=None, title=title, comment='', user=request.user, content_object=disc_sc) # TODO: find the default site_id new_threaded_comment.site_id = 1 new_threaded_comment.save() disc_sc.content_object = new_threaded_comment disc_sc.save() if not request.is_ajax(): return HttpResponseRedirect("/discussion/%d/" % new_threaded_comment.id) else: vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data = {'panel_state': 'open', 'panel_state_label': "Instructor Feedback", 'template': 'discussion', 'owners': owners, 'vocabulary': vocabulary, 'context': threaded_comment_json(request, new_threaded_comment)} return HttpResponse(json.dumps(data, indent=2), content_type='application/json')
def discussion_create(request): """Start a discussion of an arbitrary model instance.""" title = request.POST['comment_html'] # Find the object we're discussing. the_content_type = ContentType.objects.get( app_label=request.POST['app_label'], model=request.POST['model']) assert the_content_type is not None the_object = the_content_type.get_object_for_this_type( pk=request.POST['obj_pk']) assert the_object is not None try: obj_sc = Collaboration.get_associated_collab(the_object) except Collaboration.DoesNotExist: obj_sc = Collaboration() # TODO: populate this collab with sensible auth defaults. obj_sc.content_object = the_object obj_sc.save() # sky: I think what I want to do is have the ThreadedComment # point to the_object # and the collaboration will point to the threaded root comment # that way, whereas, if we live in Collaboration-land, # we can get to ThreadedComments # threaded comments can also live in it's own world without 'knowing' # about SC OTOH, threaded comment shouldn't be able # to point to the regular object # until Collaboration says it's OK (i.e. has permissions) # ISSUE: how to migrate? (see models.py) # now create the CHILD collaboration object for the discussion to point at. # This represents the auth for the discussion itself. disc_sc = Collaboration( _parent=obj_sc, title=title, # or we could point it at the root # threadedcomments object. # content_object=None, context=request.collaboration_context, ) disc_sc.policy = request.POST.get('publish', None) if request.POST.get('inherit', None) == 'true': disc_sc.group_id = obj_sc.group_id disc_sc.user_id = obj_sc.user_id disc_sc.save() # finally create the root discussion object, pointing it at the CHILD. new_threaded_comment = ThreadedComment(parent=None, title=title, comment='', user=request.user, content_object=disc_sc) # TODO: find the default site_id new_threaded_comment.site_id = 1 new_threaded_comment.save() disc_sc.content_object = new_threaded_comment disc_sc.save() if not request.is_ajax(): return HttpResponseRedirect("/discussion/%d/" % new_threaded_comment.id) else: vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data = { 'panel_state': 'open', 'panel_state_label': "Instructor Feedback", 'template': 'discussion', 'owners': owners, 'vocabulary': vocabulary, 'context': threaded_comment_json(request, new_threaded_comment) } return HttpResponse(simplejson.dumps(data, indent=2), mimetype='application/json')
def discussion_view(request, discussion_id): """Show a threadedcomments discussion of an arbitrary object. discussion_id is the pk of the root comment.""" root_comment = get_object_or_404(ThreadedComment, pk=discussion_id) if not root_comment.content_object.permission_to('read', request): return HttpResponseForbidden('You do not have permission \ to view this discussion.') try: my_course = root_comment.content_object.context.content_object except: # legacy: for when contexts weren't being set in new() my_course = request.course root_comment.content_object.context = \ Collaboration.get_associated_collab(my_course) root_comment.content_object.save() data = {'space_owner': request.user.username} if not request.is_ajax(): data['discussion'] = root_comment return render_to_response('discussions/discussion.html', data, context_instance=RequestContext(request)) else: vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data['panels'] = [{ 'panel_state': 'open', 'subpanel_state': 'open', 'panel_state_label': "Discussion", 'template': 'discussion', 'owners': owners, 'vocabulary': vocabulary, 'title': root_comment.title, 'can_edit_title': my_course.is_faculty(request.user), 'root_comment_id': root_comment.id, 'context': threaded_comment_json(request, root_comment) }] # Create a place for asset editing panel = { 'panel_state': 'closed', 'panel_state_label': "Item Details", 'template': 'asset_quick_edit', 'update_history': False, 'show_collection': False, 'owners': owners, 'vocabulary': vocabulary, 'context': { 'type': 'asset' } } data['panels'].append(panel) return HttpResponse(simplejson.dumps(data, indent=2), mimetype='application/json')
def project_workspace(request, project_id, feedback=None): """ A multi-panel editable view for the specified project Legacy note: Ideally, this function would be named project_view but StructuredCollaboration requires the view name to be <class>-view to do a reverse lookup Panel 1: Parent Assignment (if applicable) Panel 2: Project Panel 3: Instructor Feedback (if applicable & exists) Keyword arguments: project_id -- the model id """ project = get_object_or_404(Project, pk=project_id) if not project.can_read(request): return HttpResponseForbidden("forbidden") show_feedback = feedback == "feedback" data = {'space_owner': request.user.username, 'show_feedback': show_feedback} if not request.is_ajax(): data['project'] = project return render_to_response('projects/project.html', data, context_instance=RequestContext(request)) else: panels = [] vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) course = request.course is_faculty = course.is_faculty(request.user) is_assignment = project.is_assignment(request) can_edit = project.can_edit(request) feedback_discussion = project.feedback_discussion() \ if is_faculty or can_edit else None # Project Parent (assignment) if exists parent_assignment = project.assignment() if parent_assignment: assignment_context = composition_project_json( request, parent_assignment, parent_assignment.can_edit(request)) assignment_context['create_selection'] = True display = "open" if (project.title == "Untitled" and len(project.body) == 0) else "closed" panel = {'is_faculty': is_faculty, 'panel_state': display, 'subpanel_state': 'closed', 'context': assignment_context, 'owners': owners, 'vocabulary': vocabulary, 'template': 'project'} panels.append(panel) # Requested project, can be either an assignment or composition project_context = composition_project_json(request, project, can_edit) # only editing if it's new project_context['editing'] = \ True if can_edit and len(project.body) < 1 else False project_context['create_instructor_feedback'] = \ is_faculty and parent_assignment and not feedback_discussion panel = {'is_faculty': is_faculty, 'panel_state': 'closed' if show_feedback else 'open', 'context': project_context, 'template': 'project', 'owners': owners, 'vocabulary': vocabulary} panels.append(panel) # Project Response -- if the requested project is an assignment # This is primarily a student view. The student's response should # pop up automatically when the parent assignment is viewed. if is_assignment: responses = project.responses_by(request, request.user) if len(responses) > 0: response = responses[0] response_can_edit = response.can_edit(request) response_context = composition_project_json(request, response, response_can_edit) panel = {'is_faculty': is_faculty, 'panel_state': 'closed', 'context': response_context, 'template': 'project', 'owners': owners, 'vocabulary': vocabulary} panels.append(panel) if not feedback_discussion and response_can_edit: feedback_discussion = response.feedback_discussion() data['panels'] = panels # If feedback exists for the requested project if feedback_discussion: # 3rd pane is the instructor feedback, if it exists panel = {'panel_state': 'open' if show_feedback else 'closed', 'panel_state_label': "Instructor Feedback", 'template': 'discussion', 'owners': owners, 'vocabulary': vocabulary, 'context': threaded_comment_json(request, feedback_discussion)} panels.append(panel) # Create a place for asset editing panel = {'panel_state': 'closed', 'panel_state_label': "Item Details", 'template': 'asset_quick_edit', 'update_history': False, 'owners': owners, 'vocabulary': vocabulary, 'context': {'type': 'asset'}} panels.append(panel) return HttpResponse(simplejson.dumps(data, indent=2), mimetype='application/json')
def post(self, request, *args, **kwargs): """Start a discussion of an arbitrary model instance.""" title = request.POST['comment_html'] comment = request.POST.get('comment', '') # Find the object we're discussing. model = request.POST['model'] the_content_type = ContentType.objects.get( app_label=request.POST['app_label'], model=model) assert the_content_type is not None the_object = the_content_type.get_object_for_this_type( pk=request.POST['obj_pk']) assert the_object is not None try: obj_sc = Collaboration.objects.get_for_object(the_object) except Collaboration.DoesNotExist: obj_sc = Collaboration() # TODO: populate this collab with sensible auth defaults. obj_sc.content_object = the_object obj_sc.save() # sky: I think what I want to do is have the ThreadedComment # point to the_object # and the collaboration will point to the threaded root comment # that way, whereas, if we live in Collaboration-land, # we can get to ThreadedComments # threaded comments can also live in it's own world without 'knowing' # about SC OTOH, threaded comment shouldn't be able # to point to the regular object # until Collaboration says it's OK (i.e. has permissions) # ISSUE: how to migrate? (see models.py) # now create the CHILD collaboration object for the # discussion to point at. # This represents the auth for the discussion itself. collaboration_context = cached_course_collaboration(request.course) disc_sc = Collaboration( _parent=obj_sc, title=title, context=collaboration_context, ) disc_sc.set_policy(request.POST.get('publish', None)) disc_sc.save() # finally create the root discussion object, pointing it at the CHILD. new_threaded_comment = ThreadedComment(parent=None, title=title, comment=comment, user=request.user, content_object=disc_sc) # TODO: find the default site_id new_threaded_comment.site_id = 1 new_threaded_comment.save() disc_sc.content_object = new_threaded_comment disc_sc.save() DiscussionIndex.update_class_references( new_threaded_comment.comment, new_threaded_comment.user, new_threaded_comment, new_threaded_comment.content_object, new_threaded_comment.user) if not request.is_ajax(): if model == 'project': discussion_url = reverse('project-workspace', args=(request.course.pk, the_object.pk)) else: discussion_url = reverse('discussion-view', args=(request.course.pk, new_threaded_comment.id)) return HttpResponseRedirect(discussion_url) else: vocabulary = VocabularyResource().render_list( request, Vocabulary.objects.filter(course=request.course)) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) data = { 'panel_state': 'open', 'panel_state_label': "Instructor Feedback", 'template': 'discussion', 'owners': owners, 'vocabulary': vocabulary, 'context': threaded_comment_json(request, new_threaded_comment) } return HttpResponse(json.dumps(data, indent=2), content_type='application/json')
def get_records(request, record_owner, assets): course = request.course logged_in_user = request.user # Can the record_owner edit the records viewing_own_work = (record_owner == logged_in_user) viewing_faculty_records = record_owner and course.is_faculty(record_owner) # Allow the logged in user to add assets to his composition citable = ('citable' in request.GET and request.GET.get('citable') == 'true') # Is the current user faculty OR staff is_faculty = course.is_faculty(logged_in_user) # Does the course allow viewing other user selections? owner_selections_are_visible = ( course_details.all_selections_are_visible(course) or viewing_own_work or viewing_faculty_records or is_faculty) # Filter the assets for fil in filter_by: filter_value = request.GET.get(fil) if filter_value: assets = [asset for asset in assets if filter_by[fil](asset, filter_value, record_owner)] active_filters = get_active_filters(request, filter_by) # Spew out json for the assets asset_json = [] options = { 'owner_selections_are_visible': ('annotations' in request.GET and owner_selections_are_visible), 'all_selections_are_visible': course_details.all_selections_are_visible(course) or is_faculty, 'can_edit': viewing_own_work, 'citable': citable } for asset in assets: asset_json.append(gallery_asset_json(request, asset, logged_in_user, record_owner, options)) # Tags tags = [] if record_owner: if owner_selections_are_visible: # Tags for selected user tags = Tag.objects.usage_for_queryset( record_owner.sherdnote_set.filter(asset__course=course), counts=True) else: if owner_selections_are_visible: # Tags for the whole class tags = Tag.objects.usage_for_queryset( SherdNote.objects.filter(asset__course=course), counts=True) else: # Tags for myself and faculty members tags = Tag.objects.usage_for_queryset( logged_in_user.sherdnote_set.filter(asset__course=course), counts=True) for f in course.faculty: tags.extend(Tag.objects.usage_for_queryset( f.sherdnote_set.filter(asset__course=course), counts=True)) tags.sort(lambda a, b: cmp(a.name.lower(), b.name.lower())) user_resource = UserResource() owners = user_resource.render_list(request, request.course.members) # Assemble the context data = {'assets': asset_json, 'tags': [{'name': tag.name} for tag in tags], 'active_filters': active_filters, 'space_viewer': user_resource.render_one(request, logged_in_user), 'editable': viewing_own_work, 'citable': citable, 'owners': owners, 'is_faculty': is_faculty, } if record_owner: data['space_owner'] = user_resource.render_one(request, record_owner) json_stream = simplejson.dumps(data, indent=2) return HttpResponse(json_stream, mimetype='application/json')