def update_thread(request, course_id, thread_id): """ Given a course id and thread id, update a existing thread, used for both static and ajax submissions """ if 'title' not in request.POST or not request.POST['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in request.POST or not request.POST['body'].strip(): return JsonError(_("Body can't be empty")) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) thread = cc.Thread.find(thread_id) thread.body = request.POST["body"] thread.title = request.POST["title"] if "commentable_id" in request.POST: course = get_course_with_access(request.user, 'load', course_key) id_map = get_discussion_id_map(course) if request.POST.get("commentable_id") in id_map: thread.commentable_id = request.POST["commentable_id"] else: return JsonError(_("Topic doesn't exist")) thread.save() if request.is_ajax(): return ajax_content_response(request, course_key, thread.to_dict()) else: return JsonResponse(prepare_content(thread.to_dict(), course_key))
def update_thread(request, course_id, thread_id): """ Given a course id and thread id, update a existing thread, used for both static and ajax submissions """ if 'title' not in request.POST or not request.POST['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in request.POST or not request.POST['body'].strip(): return JsonError(_("Body can't be empty")) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) thread = cc.Thread.find(thread_id) thread.body = request.POST["body"] thread.title = request.POST["title"] # The following checks should avoid issues we've seen during deploys, where end users are hitting an updated server # while their browser still has the old client code. This will avoid erasing present values in those cases. if "thread_type" in request.POST: thread.thread_type = request.POST["thread_type"] if "commentable_id" in request.POST: course = get_course_with_access(request.user, 'load', course_key) id_map = get_discussion_id_map(course) if request.POST.get("commentable_id") in id_map: thread.commentable_id = request.POST["commentable_id"] else: return JsonError(_("Topic doesn't exist")) thread.save() if request.is_ajax(): return ajax_content_response(request, course_key, thread.to_dict()) else: return JsonResponse(prepare_content(thread.to_dict(), course_key))
def track_forum_event(request, event_name, course, obj, data, id_map=None): """ Send out an analytics event when a forum event happens. Works for threads, responses to threads, and comments on those responses. """ user = request.user data['id'] = obj.id if id_map is None: id_map = get_discussion_id_map(course, user) commentable_id = data['commentable_id'] if commentable_id in id_map: data['category_name'] = id_map[commentable_id]["title"] data['category_id'] = commentable_id if len(obj.body) > TRACKING_MAX_FORUM_BODY: data['truncated'] = True else: data['truncated'] = False data['body'] = obj.body[:TRACKING_MAX_FORUM_BODY] data['url'] = request.META.get('HTTP_REFERER', '') data['user_forums_roles'] = [ role.name for role in user.roles.filter(course_id=course.id) ] data['user_course_roles'] = [ role.role for role in user.courseaccessrole_set.filter(course_id=course.id) ] tracker.emit(event_name, data)
def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) post = request.POST if course.allow_anonymous: anonymous = post.get('anonymous', 'false').lower() == 'true' else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true' else: anonymous_to_peers = False if 'title' not in post or not post['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in post or not post['body'].strip(): return JsonError(_("Body can't be empty")) thread = cc.Thread( anonymous=anonymous, anonymous_to_peers=anonymous_to_peers, commentable_id=commentable_id, course_id=course_key.to_deprecated_string(), user_id=request.user.id, thread_type=post["thread_type"], body=post["body"], title=post["title"] ) # Cohort the thread if required try: group_id = get_group_id_for_comments_service(request, course_key, commentable_id) except ValueError: return HttpResponseBadRequest("Invalid cohort id") if group_id is not None: thread.group_id = group_id thread.save() # patch for backward compatibility to comments service if 'pinned' not in thread.attributes: thread['pinned'] = False follow = post.get('auto_subscribe', 'false').lower() == 'true' if follow: user = cc.User.from_django_user(request.user) user.follow(thread) event_data = get_thread_created_event_data(thread, follow) data = thread.to_dict() # Calls to id map are expensive, but we need this more than once. # Prefetch it. id_map = get_discussion_id_map(course, request.user) add_courseware_context([data], course, request.user, id_map=id_map) track_forum_event(request, THREAD_CREATED_EVENT_NAME, course, thread, event_data, id_map=id_map) if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(prepare_content(data, course_key))
def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) post = request.POST if course.allow_anonymous: anonymous = post.get('anonymous', 'false').lower() == 'true' else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true' else: anonymous_to_peers = False if 'title' not in post or not post['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in post or not post['body'].strip(): return JsonError(_("Body can't be empty")) thread = cc.Thread(anonymous=anonymous, anonymous_to_peers=anonymous_to_peers, commentable_id=commentable_id, course_id=course_key.to_deprecated_string(), user_id=request.user.id, thread_type=post["thread_type"], body=post["body"], title=post["title"]) # Cohort the thread if required try: group_id = get_group_id_for_comments_service(request, course_key, commentable_id) except ValueError: return HttpResponseBadRequest("Invalid cohort id") if group_id is not None: thread.group_id = group_id thread.save() # patch for backward compatibility to comments service if 'pinned' not in thread.attributes: thread['pinned'] = False follow = post.get('auto_subscribe', 'false').lower() == 'true' if follow: user = cc.User.from_django_user(request.user) user.follow(thread) event_data = get_thread_created_event_data(thread, follow) data = thread.to_dict() # Calls to id map are expensive, but we need this more than once. # Prefetch it. id_map = get_discussion_id_map(course, request.user) add_courseware_context([data], course, request.user, id_map=id_map) track_forum_event(request, THREAD_CREATED_EVENT_NAME, course, thread, event_data, id_map=id_map) if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(prepare_content(data, course_key))
def user_profile(request, course_id, user_id): #TODO: Allow sorting? course = get_course_with_access(request.user, course_id, 'load_forum') id_map = get_discussion_id_map(course) try: profiled_user = cc.User(id=user_id, course_id=course_id) query_params = { 'page': request.GET.get('page', 1), 'per_page': 100, # more than threads_per_page to show more activities } threads, page, num_pages = profiled_user.active_threads(query_params) thread_output = [] for thread in threads: #courseware_context = get_courseware_context(thread, course) #if courseware_context: # thread.update(courseware_context) id = thread['commentable_id'] content_info = None if id in id_map: location = id_map[id]["location"].url() title = id_map[id]["title"] url = reverse('jump_to', kwargs={"course_id": course.location.course_id, "location": location}) thread.update({"courseware_url": url, "courseware_title": title}) if len(thread.get('tags'))>0: #if thread.get('tags')[0]!='portfolio' and str(thread.get('courseware_url')).find('__am')<0: if thread.get('tags')[0]!='portfolio' and thread.get('tags')[0]!='aboutme': thread_output.append(thread) query_params['page'] = page query_params['num_pages'] = num_pages user_info = cc.User.from_django_user(request.user).to_dict() annotated_content_info = utils.get_metadata_for_threads(course_id, thread_output, request.user, user_info) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': map(utils.safe_content, thread_output), 'page': query_params['page'], 'num_pages': query_params['num_pages'], 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), }) else: context = { 'course': course, 'user': request.user, 'django_user': User.objects.get(id=user_id), 'profiled_user': profiled_user.to_dict(), 'threads': saxutils.escape(json.dumps(thread_output), escapedict), 'threads_num': len(thread_output), 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), # 'content': content, } return render_to_response('discussion/user_profile.html', context) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError, User.DoesNotExist): raise Http404
def single_thread(request, course_id, discussion_id, thread_id): course = get_course_with_access(request.user, course_id, 'load_forum') cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() try: thread = cc.Thread.find(thread_id).retrieve(recursive=True, user_id=request.user.id) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): log.error("Error loading single thread.") context = {'window_title':'Message', 'error_title':'', 'error_message':"This discussion has been deleted. <a href='/dashboard'>Click here</a> to go back to your dashboard. \ For additional help contact <a href='mailto:[email protected]'>[email protected]</a>."} return render_to_response('error.html', context) # raise Http404 if len(thread.get('children'))<1: create_comment_auto_load(request, course_id, thread_id,User.objects.get(id=thread.get("user_id"))) if request.is_ajax(): annotated_content_info = utils.get_annotated_content_infos(course_id, thread, request.user, user_info=user_info) context = {'thread': thread.to_dict(), 'course_id': course_id} # TODO: Remove completely or switch back to server side rendering # html = render_to_string('discussion/_ajax_single_thread.html', context) content = utils.safe_content(thread.to_dict()) #courseware_context = get_courseware_context(thread, course) #if courseware_context: # content.update(courseware_context) if thread.get('tags')[0]!='portfolio': id_map = get_discussion_id_map(course) id = content['commentable_id'] content_info = None if id in id_map: location = id_map[id]["location"].url() title = id_map[id]["title"] url = reverse('jump_to', kwargs={"course_id": course.location.course_id, "location": location}) content.update({"courseware_url": url, "courseware_title": title}) return utils.JsonResponse({ #'html': html, 'content': content, 'annotated_content_info': annotated_content_info, }) else: category_map = utils.get_discussion_category_map(course) id_map = get_discussion_id_map(course) try: threads, query_params = get_threads_tags(request, course_id) threads.append(thread.to_dict()) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError): log.error("Error loading single thread.") raise Http404 course = get_course_with_access(request.user, course_id, 'load_forum') thread_output = [] for thread in threads: #courseware_context = get_courseware_context(thread, course) #if courseware_context: # thread.update(courseware_context) id = thread['commentable_id'] content_info = None if id in id_map: location = id_map[id]["location"].url() title = id_map[id]["title"] url = reverse('jump_to', kwargs={"course_id": course.location.course_id, "location": location}) thread.update({"courseware_url": url, "courseware_title": title}) if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id(course_id, thread.get('group_id')).name #patch for backward compatibility with comments service if not "pinned" in thread: thread["pinned"] = False if len(thread.get('tags'))>0: #if thread.get('tags')[0]!='portfolio' and str(thread.get('courseware_url')).find('__am')<0: if thread.get('tags')[0]!='portfolio' and thread.get('tags')[0]!='aboutme': thread_output.append(thread) thread_output = [utils.safe_content(thread) for thread in thread_output] #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) #trending_tags = cc.search_trending_tags( # course_id, #) annotated_content_info = utils.get_metadata_for_threads(course_id, thread_output, request.user, user_info) cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort = get_cohort_id(request.user, course_id) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course': course, #'recent_active_threads': recent_active_threads, #'trending_tags': trending_tags, 'course_id': course.id, # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': saxutils.escape(json.dumps(thread_output), escapedict), 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_id), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'cohorts': cohorts, 'user_cohort': get_cohort_id(request.user, course_id), 'cohorted_commentables': cohorted_commentables } return render_to_response('discussion/single_thread.html', context)
def forum_form_discussion(request, course_id): """ Renders the main Discussion page, potentially filtered by a search query """ from student.models import UserTestGroup, CourseEnrollment registered=CourseEnrollment.is_enrolled(request.user, course_id) if not registered: return redirect(reverse('cabout', args=[course_id])) course = get_course_with_access(request.user, course_id, 'load_forum') category_map = utils.get_discussion_category_map(course) id_map = get_discussion_id_map(course) try: unsafethreads, query_params = get_threads_tags(request, course_id) # This might process a search query threads = [utils.safe_content(thread) for thread in unsafethreads] except cc.utils.CommentClientMaintenanceError: log.warning("Forum is in maintenance mode") return render_to_response('discussion/maintenance.html', {}) except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err: log.error("Error loading forum discussion threads: %s", str(err)) raise Http404 user = cc.User.from_django_user(request.user) user_info = user.to_dict() thread_output = [] for thread in threads: #courseware_context = get_courseware_context(thread, course) #if courseware_context: # thread.update(courseware_context) id = thread['commentable_id'] content_info = None if id in id_map: location = id_map[id]["location"].url() title = id_map[id]["title"] url = reverse('jump_to', kwargs={"course_id": course.location.course_id, "location": location}) thread.update({"courseware_url": url, "courseware_title": title}) if len(thread.get('tags'))>0: #if thread.get('tags')[0]!='portfolio' and str(thread.get('courseware_url')).find('__am')<0: if thread.get('tags')[0]!='portfolio' and thread.get('tags')[0]!='aboutme': thread_output.append(thread) annotated_content_info = utils.get_metadata_for_threads(course_id, thread_output, request.user, user_info) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': thread_output, # TODO: Standardize on 'discussion_data' vs 'threads' 'annotated_content_info': annotated_content_info, 'num_pages': query_params['num_pages'], 'page': query_params['page'], }) else: #recent_active_threads = cc.search_recent_active_threads( # course_id, # recursive=False, # query_params={'follower_id': request.user.id}, #) #trending_tags = cc.search_trending_tags( # course_id, #) cohorts = get_course_cohorts(course_id) cohorted_commentables = get_cohorted_commentables(course_id) user_cohort_id = get_cohort_id(request.user, course_id) if request.GET.get('pf_id') != None: curr_user = User.objects.get(id=int(request.GET.get('pf_id'))) else: curr_user = None context = { 'curr_user':curr_user, 'csrf': csrf(request)['csrf_token'], 'course': course, #'recent_active_threads': recent_active_threads, #'trending_tags': trending_tags, 'staff_access': has_access(request.user, course, 'staff'), 'threads': saxutils.escape(json.dumps(thread_output), escapedict), 'thread_pages': query_params['num_pages'], 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, course, 'staff'), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'course_id': course.id, 'category_map': category_map, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'is_moderator': cached_has_permission(request.user, "see_all_cohorts", course_id), 'cohorts': cohorts, 'user_cohort': user_cohort_id, 'cohorted_commentables': cohorted_commentables, 'is_course_cohorted': is_course_cohorted(course_id) } # print "start rendering.." return render_to_response('discussion/index.html', context)
def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) post = request.POST if course.allow_anonymous: anonymous = post.get('anonymous', 'false').lower() == 'true' else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true' else: anonymous_to_peers = False if 'title' not in post or not post['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in post or not post['body'].strip(): return JsonError(_("Body can't be empty")) thread = cc.Thread( anonymous=anonymous, anonymous_to_peers=anonymous_to_peers, commentable_id=commentable_id, course_id=course_key.to_deprecated_string(), user_id=request.user.id, thread_type=post["thread_type"], body=post["body"], title=post["title"] ) # Cohort the thread if required try: group_id = get_group_id_for_comments_service(request, course_key, commentable_id) except ValueError: return HttpResponseBadRequest("Invalid cohort id") if group_id is not None: thread.group_id = group_id thread.save() # patch for backward compatibility to comments service if 'pinned' not in thread.attributes: thread['pinned'] = False follow = post.get('auto_subscribe', 'false').lower() == 'true' if follow: user = cc.User.from_django_user(request.user) user.follow(thread) event_data = get_thread_created_event_data(thread, follow) data = thread.to_dict() # Calls to id map are expensive, but we need this more than once. # Prefetch it. id_map = get_discussion_id_map(course, request.user) add_courseware_context([data], course, request.user, id_map=id_map) track_forum_event(request, THREAD_CREATED_EVENT_NAME, course, thread, event_data, id_map=id_map) try: role = Role.objects.get(name="E-tutor", course_id=course_id) etutors = role.users.all().order_by('username') user= request.user """ Send Notify to the Etutors ! """ subject = "nouveau message sur le forum" message = "Bonjour,\n\n Il y a un nouveau message sur Universite Talan.\n\n--------------------\n--------------------\n\n Auteur : "+ user.username +" \n Titre : "+ post["title"] + " \n Message : "+post["body"] from_email = "*****@*****.**" def extract_etutors_info(etutor): if etutor.email != user.email: send_mail("[UnivTalan] "+subject, message, from_email, [etutor.email]) map(extract_etutors_info, etutors) except Role.DoesNotExist: etutors = [] if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(prepare_content(data, course_key))
def create_thread(request, course_id, commentable_id): """ Given a course and commentble ID, create the thread """ log.debug("Creating new thread in %r, id %r", course_id, commentable_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) post = request.POST if course.allow_anonymous: anonymous = post.get('anonymous', 'false').lower() == 'true' else: anonymous = False if course.allow_anonymous_to_peers: anonymous_to_peers = post.get('anonymous_to_peers', 'false').lower() == 'true' else: anonymous_to_peers = False if 'title' not in post or not post['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in post or not post['body'].strip(): return JsonError(_("Body can't be empty")) thread = cc.Thread( anonymous=anonymous, anonymous_to_peers=anonymous_to_peers, commentable_id=commentable_id, course_id=course_key.to_deprecated_string(), user_id=request.user.id, thread_type=post["thread_type"], body=post["body"], title=post["title"] ) # Cohort the thread if required try: group_id = get_group_id_for_comments_service(request, course_key, commentable_id) except ValueError: return HttpResponseBadRequest("Invalid cohort id") if group_id is not None: thread.group_id = group_id thread.save() # patch for backward compatibility to comments service if 'pinned' not in thread.attributes: thread['pinned'] = False follow = post.get('auto_subscribe', 'false').lower() == 'true' user = cc.User.from_django_user(request.user) if follow: user.follow(thread) event_data = get_thread_created_event_data(thread, follow) data = thread.to_dict() # Calls to id map are expensive, but we need this more than once. # Prefetch it. id_map = get_discussion_id_map(course, request.user) add_courseware_context([data], course, request.user, id_map=id_map) track_forum_event(request, THREAD_CREATED_EVENT_NAME, course, thread, event_data, id_map=id_map) if thread.get('group_id'): # Send a notification message, if enabled, when anyone posts a new thread on # a cohorted/private discussion, except the poster him/herself _send_discussion_notification( 'open-edx.lms.discussions.cohorted-thread-added', unicode(course_key), thread, request.user, excerpt=_get_excerpt(thread.body), recipient_group_id=thread.get('group_id'), recipient_exclude_user_ids=[request.user.id], is_anonymous_user=anonymous or anonymous_to_peers ) # call into the social_engagement django app to # rescore this user _update_user_engagement_score(course_key, request.user.id) add_thread_group_name(data, course_key) if thread.get('group_id') and not thread.get('group_name'): thread['group_name'] = get_cohort_by_id(course_key, thread.get('group_id')).name data = thread.to_dict() if request.is_ajax(): return ajax_content_response(request, course_key, data) else: return JsonResponse(prepare_content(data, course_key))
def get_threads(request, course, discussion_id=None, per_page=THREADS_PER_PAGE): """ This may raise an appropriate subclass of cc.utils.CommentClientError if something goes wrong, or ValueError if the group_id is invalid. """ default_query_params = { 'page': 1, 'per_page': per_page, 'sort_key': 'date', 'sort_order': 'desc', 'text': '', 'course_id': unicode(course.id), 'user_id': request.user.id, 'group_id': get_group_id_for_comments_service( request, course.id, discussion_id), # may raise ValueError } if discussion_id is not None: default_query_params['commentable_id'] = discussion_id else: default_query_params['commentable_ids'] = ','.join( course.top_level_discussion_topic_ids + utils.get_discussion_id_map(course, request.user).keys()) if not request.GET.get('sort_key'): # If the user did not select a sort key, use their last used sort key cc_user = cc.User.from_django_user(request.user) cc_user.retrieve() # TODO: After the comment service is updated this can just be user.default_sort_key because the service returns the default value default_query_params['sort_key'] = cc_user.get( 'default_sort_key') or default_query_params['sort_key'] else: # If the user clicked a sort key, update their default sort key cc_user = cc.User.from_django_user(request.user) cc_user.default_sort_key = request.GET.get('sort_key') cc_user.save() #there are 2 dimensions to consider when executing a search with respect to group id #is user a moderator #did the user request a group query_params = merge_dict( default_query_params, strip_none( extract(request.GET, [ 'page', 'sort_key', 'sort_order', 'text', 'commentable_ids', 'flagged', 'unread', 'unanswered', ]))) threads, page, num_pages, corrected_text = cc.Thread.search(query_params) for thread in threads: # patch for backward compatibility to comments service if 'pinned' not in thread: thread['pinned'] = False query_params['page'] = page query_params['num_pages'] = num_pages query_params['corrected_text'] = corrected_text return threads, query_params
def user_discussions_profile(request, course_id, portfolio_user): course = get_course_with_access(portfolio_user, course_id, 'load_forum') id_map = get_discussion_id_map(course) try: profiled_user = cc.User(id=portfolio_user.id, course_id=course_id) query_params = { 'page': 1, 'per_page': 100, # more than threads_per_page to show more activities } threads, page, num_pages = profiled_user.active_threads(query_params) thread_output = [] for thread in threads: #courseware_context = get_courseware_context(thread, course) #if courseware_context: # thread.update(courseware_context) id = thread['commentable_id'] content_info = None if id in id_map: location = id_map[id]["location"].url() title = id_map[id]["title"] url = reverse('jump_to', kwargs={"course_id": course.location.course_id, "location": location}) thread.update({"courseware_url": url, "courseware_title": title}) if thread.get('tags')[0]!='portfolio' and thread.get('tags')[0]!='aboutme': thread_output.append(thread) query_params['page'] = page query_params['num_pages'] = num_pages user_info = cc.User.from_django_user(portfolio_user).to_dict() annotated_content_info = utils.get_metadata_for_threads(course_id, thread_output, portfolio_user, user_info) if request.is_ajax(): return utils.JsonResponse({ 'discussion_data': map(utils.safe_content, thread_output), 'page': query_params['page'], 'num_pages': query_params['num_pages'], 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), }) else: context = { 'course': course, 'curr_user':portfolio_user, 'user': request.user, 'django_user': User.objects.get(id=portfolio_user.id), 'profiled_user': profiled_user.to_dict(), 'threads': saxutils.escape(json.dumps(thread_output), escapedict), 'threads_num': len(thread_output), 'user_info': saxutils.escape(json.dumps(user_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'portfolio_user':portfolio_user, 'portfolio_user_id':portfolio_user.id, 'roles': saxutils.escape(json.dumps(utils.get_role_ids(course_id)), escapedict), 'flag_moderator': cached_has_permission(portfolio_user, 'openclose_thread', course.id) or has_access(portfolio_user, course, 'staff'), } return context except: raise Http404