def _render_invalid_certificate(request, course_id, platform_name, configuration, cert_path=INVALID_CERTIFICATE_TEMPLATE_PATH): """ Renders the invalid certificate view with default header and footer. """ context = {} _update_context_with_basic_info(context, course_id, platform_name, configuration) # Add certificate header/footer data to current context context.update( get_certificate_header_context(is_secure=request.is_secure())) context.update(get_certificate_footer_context()) return render_to_response(cert_path, context)
def render_press_release(request, slug): """ Render a press release given a slug. Similar to the "render" function above, but takes a slug and does a basic conversion to convert it to a template file. a) all lower case, b) convert dashes to underscores, and c) appending ".html" """ template = slug.lower().replace('-', '_') + ".html" try: resp = render_to_response( 'static_templates/press_releases/' + template, {}) except TemplateDoesNotExist: raise Http404 # lint-amnesty, pylint: disable=raise-missing-from else: return resp
def _render_valid_certificate(request, context, custom_template=None): """ Renders certificate """ if custom_template: template = Template( custom_template.template, output_encoding='utf-8', input_encoding='utf-8', default_filters=['decode.utf8'], encoding_errors='replace', ) context = RequestContext(request, context) return HttpResponse(template.render(context)) else: return render_to_response("certificates/valid.html", context)
def _asset_index(request, course_key): ''' Display an editable asset library. Supports start (0-based index into the list of assets) and max query parameters. ''' course_module = modulestore().get_course(course_key) return render_to_response('asset_index.html', { 'language_code': request.LANGUAGE_CODE, 'context_course': course_module, 'max_file_size_in_mbs': settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB, 'chunk_size_in_mbs': settings.UPLOAD_CHUNK_SIZE_IN_MB, 'max_file_size_redirect_url': settings.MAX_ASSET_UPLOAD_FILE_SIZE_URL, 'asset_callback_url': reverse_course_url('assets_handler', course_key) })
def program_listing(request): """View a list of programs in which the user is engaged.""" programs_config = ProgramsApiConfig.current() programs_fragment = ProgramsFragmentView().render_to_fragment( request, programs_config=programs_config) context = { 'disable_courseware_js': True, 'programs_fragment': programs_fragment, 'nav_hidden': True, 'show_dashboard_tabs': True, 'show_program_listing': programs_config.enabled, 'uses_bootstrap': True, } return render_to_response('learner_dashboard/programs.html', context)
def dashboard(request, course, ccx=None): """ Display the CCX Coach Dashboard. """ # right now, we can only have one ccx per user and course # so, if no ccx is passed in, we can sefely redirect to that if ccx is None: ccx = get_ccx_for_coach(course, request.user) if ccx: url = reverse( 'ccx_coach_dashboard', kwargs={'course_id': CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))} ) return redirect(url) context = { 'course': course, 'ccx': ccx, } context.update(get_ccx_creation_dict(course)) if ccx: ccx_locator = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id)) # At this point we are done with verification that current user is ccx coach. assign_staff_role_to_ccx(ccx_locator, request.user, course.id) schedule = get_ccx_schedule(course, ccx) grading_policy = get_override_for_ccx( ccx, course, 'grading_policy', course.grading_policy) context['schedule'] = json.dumps(schedule, indent=4) context['save_url'] = reverse( 'save_ccx', kwargs={'course_id': ccx_locator}) context['ccx_members'] = CourseEnrollment.objects.filter(course_id=ccx_locator, is_active=True) context['gradebook_url'] = reverse( 'ccx_gradebook', kwargs={'course_id': ccx_locator}) context['grades_csv_url'] = reverse( 'ccx_grades_csv', kwargs={'course_id': ccx_locator}) context['grading_policy'] = json.dumps(grading_policy, indent=4) context['grading_policy_url'] = reverse( 'ccx_set_grading_policy', kwargs={'course_id': ccx_locator}) with ccx_course(ccx_locator) as course: # lint-amnesty, pylint: disable=redefined-argument-from-local context['course'] = course else: context['create_ccx_url'] = reverse( 'create_ccx', kwargs={'course_id': course.id}) return render_to_response('ccx/coach_dashboard.html', context)
def tabs_handler(request, course_key_string): """ The restful handler for static tabs. GET html: return page for editing static tabs json: not supported PUT or POST json: update the tab order. It is expected that the request body contains a JSON-encoded dict with entry "tabs". The value for "tabs" is an array of tab locators, indicating the desired order of the tabs. Creating a tab, deleting a tab, or changing its contents is not supported through this method. Instead use the general xblock URL (see item.xblock_handler). """ course_key = CourseKey.from_string(course_key_string) if not has_course_author_access(request.user, course_key): raise PermissionDenied() course_item = modulestore().get_course(course_key) if "application/json" in request.META.get("HTTP_ACCEPT", "application/json"): if request.method == "GET": # lint-amnesty, pylint: disable=no-else-raise raise NotImplementedError("coming soon") else: try: update_tabs_handler(course_item, request.json, request.user) except ValidationError as err: return JsonResponseBadRequest(err.detail) return JsonResponse() elif request.method == "GET": # assume html # get all tabs from the tabs list: static tabs (a.k.a. user-created tabs) and built-in tabs # present in the same order they are displayed in LMS tabs_to_render = list(get_course_tabs(course_item, request.user)) return render_to_response( "edit-tabs.html", { "context_course": course_item, "tabs_to_render": tabs_to_render, "lms_link": get_lms_link_for_item(course_item.location), }, ) else: return HttpResponseNotFound()
def render(self, request): """ Render the index page. """ self._prefetch_and_bind_course(request) if self.course.has_children_at_depth(CONTENT_DEPTH): self._reset_section_to_exam_if_required() self.chapter = self._find_chapter() self.section = self._find_section() if self.chapter and self.section: self._redirect_if_not_requested_section() self._save_positions() self._prefetch_and_bind_section() self._redirect_to_learning_mfe() check_content_start_date_for_masquerade_user(self.course_key, self.effective_user, request, self.course.start, self.chapter.start, self.section.start) if not request.user.is_authenticated: qs = urllib.parse.urlencode({ 'course_id': self.course_key, 'enrollment_action': 'enroll', 'email_opt_in': False, }) allow_anonymous = check_public_access(self.course, [COURSE_VISIBILITY_PUBLIC]) if not allow_anonymous: PageLevelMessages.register_warning_message( request, Text(_("You are not signed in. To see additional course content, {sign_in_link} or " "{register_link}, and enroll in this course.")).format( sign_in_link=HTML('<a href="{url}">{sign_in_label}</a>').format( sign_in_label=_('sign in'), url='{}?{}'.format(reverse('signin_user'), qs), ), register_link=HTML('<a href="/{url}">{register_label}</a>').format( register_label=_('register'), url='{}?{}'.format(reverse('register_user'), qs), ), ) ) return render_to_response('courseware/courseware.html', self._create_courseware_context(request))
def edxnotes(request, course_id): """ Displays the EdxNotes page. Arguments: request: HTTP request object course_id: course id Returns: Rendered HTTP response. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) if not is_feature_enabled(course, request.user): raise Http404 notes_info = get_notes(request, course) has_notes = (len(notes_info.get('results')) > 0) context = { "course": course, "notes_endpoint": reverse("notes", kwargs={"course_id": course_id}), "notes": notes_info, "page_size": DEFAULT_PAGE_SIZE, "debug": settings.DEBUG, 'position': None, 'disabled_tabs': settings.NOTES_DISABLED_TABS, 'has_notes': has_notes, } if not has_notes: field_data_cache = FieldDataCache.cache_for_descriptor_descendents( course.id, request.user, course, depth=2) course_module = get_module_for_descriptor(request.user, request, course, field_data_cache, course_key, course=course) position = get_course_position(course_module) if position: context.update({ 'position': position, }) return render_to_response("edxnotes/edxnotes.html", context)
def program_details(request, program_uuid): """View details about a specific program.""" programs_config = ProgramsApiConfig.current() program_fragment = ProgramDetailsFragmentView().render_to_fragment( request, program_uuid, programs_config=programs_config ) context = { 'program_fragment': program_fragment, 'show_program_listing': programs_config.enabled, 'show_dashboard_tabs': True, 'nav_hidden': True, 'disable_courseware_js': True, 'uses_bootstrap': True, } return render_to_response('learner_dashboard/program_details.html', context)
def library_blocks_view(library, user, response_format): """ The main view of a course's content library. Shows all the XBlocks in the library, and allows adding/editing/deleting them. Can be called with response_format="json" to get a JSON-formatted list of the XBlocks in the library along with library metadata. Assumes that read permissions have been checked before calling this. """ assert isinstance(library.location.library_key, LibraryLocator) assert isinstance(library.location, LibraryUsageLocator) children = library.children if response_format == "json": # The JSON response for this request is short and sweet: prev_version = library.runtime.course_entry.structure[ 'previous_version'] return JsonResponse({ "display_name": library.display_name, "library_id": str(library.location.library_key), "version": str(library.runtime.course_entry.course_key.version_guid), "previous_version": str(prev_version) if prev_version else None, "blocks": [str(x) for x in children], }) can_edit = has_studio_write_access(user, library.location.library_key) xblock_info = create_xblock_info(library, include_ancestor_info=False, graders=[]) component_templates = get_component_templates( library, library=True) if can_edit else [] return render_to_response( 'library.html', { 'can_edit': can_edit, 'context_library': library, 'component_templates': component_templates, 'xblock_info': xblock_info, 'templates': CONTAINER_TEMPLATES, })
def export_handler(request, course_key_string): """ The restful handler for exporting a course. GET html: return html page for import page json: not supported POST Start a Celery task to export the course The Studio UI uses a POST request to start the export asynchronously, with a link appearing on the page once it's ready. """ course_key = CourseKey.from_string(course_key_string) if not has_course_author_access(request.user, course_key): raise PermissionDenied() if isinstance(course_key, LibraryLocator): courselike_module = modulestore().get_library(course_key) context = { 'context_library': courselike_module, 'courselike_home_url': reverse_library_url("library_handler", course_key), 'library': True } else: courselike_module = modulestore().get_course(course_key) if courselike_module is None: raise Http404 context = { 'context_course': courselike_module, 'courselike_home_url': reverse_course_url("course_handler", course_key), 'library': False } context['status_url'] = reverse_course_url('export_status_handler', course_key) # an _accept URL parameter will be preferred over HTTP_ACCEPT in the header. requested_format = request.GET.get('_accept', request.META.get('HTTP_ACCEPT', 'text/html')) if request.method == 'POST': export_olx.delay(request.user.id, course_key_string, request.LANGUAGE_CODE) return JsonResponse({'ExportStatus': 1}) elif 'text/html' in requested_format: return render_to_response('export.html', context) else: # Only HTML request format is supported (no JSON). return HttpResponse(status=406)
def get(self, request): """ Based on the query string parameters passed through the GET request Search the data store for information about ProgramEnrollment and SSO linkage with the user. """ search_error = '' edx_username_or_email = request.GET.get('edx_user', '').strip() org_key = request.GET.get('org_key', '').strip() external_user_key = request.GET.get('external_user_key', '').strip() learner_program_enrollments = {} saml_providers_with_org_key = self._get_org_keys_and_idps() selected_provider = None if org_key: selected_provider = saml_providers_with_org_key.get(org_key) if edx_username_or_email: learner_program_enrollments, search_error = self._get_account_info( edx_username_or_email, selected_provider, ) elif org_key and external_user_key: learner_program_enrollments = self._get_external_user_info( external_user_key, org_key, selected_provider, ) if not learner_program_enrollments: search_error = 'No user found for external key {} for institution {}'.format( external_user_key, org_key) elif not org_key and not external_user_key: # This is initial rendering state. pass else: search_error = ( "To perform a search, you must provide either the student's " "(a) edX username, " "(b) email address associated with their edX account, or " "(c) Identity-providing institution and external key!") return render_to_response( self.CONSOLE_TEMPLATE_PATH, { 'error': search_error, 'learner_program_enrollments': learner_program_enrollments, 'org_keys': sorted(saml_providers_with_org_key.keys()), })
def manage_library_users(request, library_key_string): """ Studio UI for editing the users within a library. Uses the /course_team/:library_key/:user_email/ REST API to make changes. """ library_key = CourseKey.from_string(library_key_string) if not isinstance(library_key, LibraryLocator): raise Http404 # This is not a library user_perms = get_user_permissions(request.user, library_key) if not user_perms & STUDIO_VIEW_USERS: raise PermissionDenied() library = modulestore().get_library(library_key) if library is None: raise Http404 # Segment all the users explicitly associated with this library, ensuring each user only has one role listed: instructors = set(CourseInstructorRole(library_key).users_with_role()) staff = set(CourseStaffRole(library_key).users_with_role()) - instructors users = set( LibraryUserRole(library_key).users_with_role()) - instructors - staff formatted_users = [] for user in instructors: formatted_users.append(user_with_role(user, 'instructor')) for user in staff: formatted_users.append(user_with_role(user, 'staff')) for user in users: formatted_users.append(user_with_role(user, 'library_user')) return render_to_response( 'manage_users_lib.html', { 'context_library': library, 'users': formatted_users, 'allow_actions': bool(user_perms & STUDIO_EDIT_ROLES), 'library_key': str(library_key), 'lib_users_url': reverse_library_url('manage_library_users', library_key_string), 'show_children_previews': library.show_children_previews })
def checklists_handler(request, course_key_string=None): ''' The restful handler for course checklists. It allows retrieval of the checklists (as an HTML page). GET html: return an html page which will show course checklists. Note that only the checklists container is returned and that the actual data is determined with a client-side request. ''' course_key = CourseKey.from_string(course_key_string) if not has_course_author_access(request.user, course_key): raise PermissionDenied() course_module = modulestore().get_course(course_key) return render_to_response('checklists.html', { 'language_code': request.LANGUAGE_CODE, 'context_course': course_module, })
def post(self, request, catalog_id): """Update or delete this catalog.""" client = self.get_catalog_api_client(request.user) if request.POST.get('delete-catalog') == 'on': client.catalogs(catalog_id).delete() return redirect(reverse('api_admin:catalog-search')) form = CatalogForm(request.POST) if not form.is_valid(): response = client.catalogs(catalog_id).get() catalog = Catalog(attributes=response) return render_to_response(self.template_name, self.get_context_data( catalog, form, client), status=400) catalog = client.catalogs(catalog_id).patch(form.cleaned_data) return redirect( reverse('api_admin:catalog-edit', kwargs={'catalog_id': catalog['id']}))
def inner(request, *args, **kwargs): """ Execute the function in try..except block and return custom server-error page in case of unhandled exception """ try: return func(request, *args, **kwargs) except Exception: # pylint: disable=broad-except if settings.DEBUG: # lint-amnesty, pylint: disable=no-else-raise # In debug mode let django process the 500 errors and display debug info for the developer raise elif test_func is None or test_func(request): # Display custom 500 page if either # 1. test_func is None (meaning nothing to test) # 2. or test_func(request) returns True log.exception("Error in django view.") return render_to_response(template_path, context) else: # Do not show custom 500 error when test fails raise
def post(self, request, username): """ Create a new catalog for a user. """ form = CatalogForm(request.POST) client = self.get_catalog_api_client(request.user) if not form.is_valid(): return render_to_response(self.template, self.get_context_data( client, username, form), status=400) attrs = form.cleaned_data response = client.post(self.catalogs_api_url, data=attrs) response.raise_for_status() catalog = response.json() return redirect( reverse('api_admin:catalog-edit', kwargs={'catalog_id': catalog['id']}))
def import_handler(request, course_key_string): """ The restful handler for importing a course. GET html: return html page for import page json: not supported POST or PUT json: import a course via the .tar.gz file specified in request.FILES """ courselike_key = CourseKey.from_string(course_key_string) library = isinstance(courselike_key, LibraryLocator) if library: successful_url = reverse_library_url('library_handler', courselike_key) context_name = 'context_library' courselike_module = modulestore().get_library(courselike_key) else: successful_url = reverse_course_url('course_handler', courselike_key) context_name = 'context_course' courselike_module = modulestore().get_course(courselike_key) if not has_course_author_access(request.user, courselike_key): raise PermissionDenied() if 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'): if request.method == 'GET': # lint-amnesty, pylint: disable=no-else-raise raise NotImplementedError('coming soon') else: return _write_chunk(request, courselike_key) elif request.method == 'GET': # assume html status_url = reverse_course_url("import_status_handler", courselike_key, kwargs={'filename': "fillerName"}) return render_to_response( 'import.html', { context_name: courselike_module, 'successful_import_redirect_url': successful_url, 'import_status_url': status_url, 'library': isinstance(courselike_key, LibraryLocator) }) else: return HttpResponseNotFound()
def get(self, request): # lint-amnesty, pylint: disable=missing-function-docstring # If ZENDESK_URL is not defined, then it will redirect to the static contact page # to avoid 500 error that arises due to missing Zendesk configuration if not settings.ZENDESK_URL: return redirect('contact') if not configuration_helpers.get_value('CONTACT_US_PAGE', True): raise Http404 context = { 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), 'support_email': configuration_helpers.get_value('CONTACT_EMAIL', settings.CONTACT_EMAIL), 'custom_fields': settings.ZENDESK_CUSTOM_FIELDS } # Tag all issues with LMS to distinguish channel which received the request tags = ['LMS'] # Per edX support, we would like to be able to route feedback items by site via tagging current_site_name = configuration_helpers.get_value("SITE_NAME") if current_site_name: current_site_name = current_site_name.replace(".", "_") tags.append(f"site_name_{current_site_name}") if request.user.is_authenticated: context['course_id'] = request.session.get('course_id', '') context[ 'user_enrollments'] = CourseEnrollment.enrollments_for_user_with_overviews_preload( request.user) enterprise_customer = enterprise_api.enterprise_customer_for_request( request) if enterprise_customer: tags.append('enterprise_learner') context['tags'] = tags return render_to_response("support/contact_us.html", context)
def spoc_gradebook(request, course_id): """ Show the gradebook for this course: - Only shown for courses with enrollment < settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS") - Only displayed to course staff """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'staff', course_key, depth=None) student_info, page = get_grade_book_page(request, course, course_key) return render_to_response('courseware/gradebook.html', { 'page': page, 'page_url': reverse('spoc_gradebook', kwargs={'course_id': str(course_key)}), 'students': student_info, 'course': course, 'course_id': course_key, # Checked above 'staff_access': True, 'ordered_grades': sorted(list(course.grade_cutoffs.items()), key=lambda i: i[1], reverse=True), })
def html_index(request, course_id, book_index, chapter=None): """ Display an HTML textbook. course_id: course for which to display text. The course should have "html_textbooks" property defined. book index: zero-based index of which HTML textbook to display. chapter: (optional) one-based index into the chapter array of textbook HTML files to display. Defaults to first chapter. Specifying this assumes that there are separate HTML files for each chapter in a textbook. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key) staff_access = bool(has_access(request.user, 'staff', course)) book_index = int(book_index) if book_index < 0 or book_index >= len(course.html_textbooks): raise Http404(u"Invalid book index value: {0}".format(book_index)) textbook = course.html_textbooks[book_index] if 'url' in textbook: textbook['url'] = remap_static_url(textbook['url'], course) # then remap all the chapter URLs as well, if they are provided. if 'chapters' in textbook: for entry in textbook['chapters']: entry['url'] = remap_static_url(entry['url'], course) student = request.user return render_to_response( 'static_htmlbook.html', { 'book_index': book_index, 'course': course, 'textbook': textbook, 'chapter': chapter, 'student': student, 'staff_access': staff_access, }, )
def ccx_gradebook(request, course, ccx=None): """ Show the gradebook for this CCX. """ if not ccx: raise Http404 ccx_key = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id)) with ccx_course(ccx_key) as course: # lint-amnesty, pylint: disable=redefined-argument-from-local student_info, page = get_grade_book_page(request, course, course_key=ccx_key) return render_to_response('courseware/gradebook.html', { 'page': page, 'page_url': reverse('ccx_gradebook', kwargs={'course_id': ccx_key}), 'students': student_info, 'course': course, 'course_id': course.id, 'staff_access': request.user.is_staff, 'ordered_grades': sorted( list(course.grade_cutoffs.items()), key=lambda i: i[1], reverse=True), })
def manage_user_standing(request): """ Renders the view used to manage user standing. Also displays a table of user accounts that have been disabled and who disabled them. """ if not request.user.is_staff: raise Http404 all_disabled_accounts = UserStanding.objects.filter( account_status=UserStanding.ACCOUNT_DISABLED) all_disabled_users = [standing.user for standing in all_disabled_accounts] headers = ['username', 'account_changed_by'] rows = [] for user in all_disabled_users: row = [user.username, user.standing.changed_by] rows.append(row) context = {'headers': headers, 'rows': rows} return render_to_response("manage_user_standing.html", context)
def custon_certificate_view(request,certificate_uuid): context = {} try: certificate = GeneratedCertificate.eligible_certificates.get( verify_uuid=certificate_uuid, status=CertificateStatuses.downloadable ) course_overview = CourseOverview.objects.get(id=certificate.course_id) user = User.objects.get(id=certificate.user.id) course_exinfo = course_extrainfo.objects.get(course_id=course_overview.id) category_name = categories.objects.get(id=course_exinfo.category) context['certificate_username'] = user.username context['certificate_created'] = certificate.created_date context['course_name'] = course_overview.display_name context['category'] = category_name certificate_img_obj = CustomCertificatePath.objects.filter(verify_uuid=certificate_uuid) if certificate_img_obj: context['certificate_img_path'] = certificate_img_obj[0].file_path else: context['certificate_img_path'] = '' if course_exinfo.microsite_visibile_only == '0': context['course_about_url'] = 'https://docmode.org/' + course_exinfo.course_seo_url else: site_config_obj = SiteConfiguration.objects.filter(site=request.site) if site_config_obj: site_url = site_config_obj[0].get_value('MKTG_URLS') sites_url = site_url['ROOT'] context['course_about_url'] = sites_url +'/view/' + course_exinfo.course_seo_url else: context['course_about_url'] = 'https://docmode.org/' + course_exinfo.course_seo_url context['course_enrollments'] = len(CourseEnrollment.objects.filter(course_id=course_overview.id)) if request.user.id == user.id: context['certificate_url'] = reverse('certificates:render_cert_by_uuid', kwargs={'certificate_uuid': certificate_uuid}) context['global_share_option_url'] = settings.LMS_ROOT_URL + '/accomplishments/' + certificate_uuid else: context['certificate_url'] = '' context['global_share_option_url'] = '' return render_to_response("certificates/custom_certificate.html", context) except GeneratedCertificate.DoesNotExist: raise Http404
def get(self, request): # lint-amnesty, pylint: disable=arguments-differ """Displays course Enrollment and staffing course statistics""" if not request.user.is_staff: raise Http404 data = [] for course in self.get_courses(): datum = [course.display_name, course.id] datum += [ CourseEnrollment.objects.filter(course_id=course.id).count() ] datum += [CourseStaffRole(course.id).users_with_role().count()] datum += [ ','.join([ x.username for x in CourseInstructorRole(course.id).users_with_role() ]) ] data.append(datum) datatable = dict(header=[ _('Course Name'), _('course_id'), _('# enrolled'), _('# staff'), _('instructors') ], title=_('Enrollment information for all courses'), data=data) context = { 'datatable': datatable, 'msg': self.msg, 'djangopid': os.getpid(), 'modeflag': { 'staffing': 'active-section' }, } return render_to_response(self.template_name, context)
def get(self, request): if not configuration_helpers.get_value('CONTACT_US_PAGE', True): raise Http404 context = { 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), 'support_email': configuration_helpers.get_value('CONTACT_EMAIL', settings.CONTACT_EMAIL), 'custom_fields': settings.ZENDESK_CUSTOM_FIELDS } # Tag all issues with LMS to distinguish channel which received the request tags = ['LMS'] # Per edX support, we would like to be able to route feedback items by site via tagging current_site_name = configuration_helpers.get_value("SITE_NAME") if current_site_name: current_site_name = current_site_name.replace(".", "_") tags.append("site_name_{site}".format(site=current_site_name)) if request.user.is_authenticated: context['course_id'] = request.session.get('course_id', '') context[ 'user_enrollments'] = CourseEnrollment.enrollments_for_user_with_overviews_preload( request.user) enterprise_customer = enterprise_api.enterprise_customer_for_request( request) if enterprise_customer: tags.append('enterprise_learner') context['tags'] = tags return render_to_response("support/contact_us.html", context)
def render(request, template): """ This view function renders the template sent without checking that it exists. Do not expose template as a regex part of the url. The user should not be able to ender any arbitray template name. The correct usage would be: url(r'^jobs$', 'static_template_view.views.render', {'template': 'jobs.html'}, name="jobs") """ # Guess content type from file extension content_type, __ = mimetypes.guess_type(template) try: context = {} # This is necessary for the dialog presented with the TOS in /register if template == 'honor.html': context['allow_iframing'] = True # Format Examples: static_template_about_header configuration_base = 'static_template_' + template.replace( '.html', '').replace('-', '_') page_header = configuration_helpers.get_value(configuration_base + '_header') page_content = configuration_helpers.get_value(configuration_base + '_content') if page_header: context['page_header'] = mark_safe(page_header) if page_content: context['page_content'] = mark_safe(page_content) result = render_to_response('static_templates/' + template, context, content_type=content_type) return result except TopLevelLookupException: raise Http404 # lint-amnesty, pylint: disable=raise-missing-from except TemplateDoesNotExist: raise Http404 # lint-amnesty, pylint: disable=raise-missing-from
def create_ccx(request, course, ccx=None): """ Create a new CCX """ name = request.POST.get('name') if hasattr(course, 'ccx_connector') and course.ccx_connector: # if ccx connector url is set in course settings then inform user that he can # only create ccx by using ccx connector url. context = get_ccx_creation_dict(course) messages.error(request, context['use_ccx_con_error_message']) return render_to_response('ccx/coach_dashboard.html', context) # prevent CCX objects from being created for deprecated course ids. if course.id.deprecated: messages.error( request, _("You cannot create a CCX from a course using a deprecated id. " "Please create a rerun of this course in the studio to allow " "this action.")) url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id}) return redirect(url) ccx = CustomCourseForEdX(course_id=course.id, coach=request.user, display_name=name) ccx.save() # Make sure start/due are overridden for entire course start = TODAY().replace(tzinfo=pytz.UTC) override_field_for_ccx(ccx, course, 'start', start) override_field_for_ccx(ccx, course, 'due', None) # Enforce a static limit for the maximum amount of students that can be enrolled override_field_for_ccx(ccx, course, 'max_student_enrollments_allowed', settings.CCX_MAX_STUDENTS_ALLOWED) # Save display name explicitly override_field_for_ccx(ccx, course, 'display_name', name) # Hide anything that can show up in the schedule hidden = 'visible_to_staff_only' for chapter in course.get_children(): override_field_for_ccx(ccx, chapter, hidden, True) for sequential in chapter.get_children(): override_field_for_ccx(ccx, sequential, hidden, True) for vertical in sequential.get_children(): override_field_for_ccx(ccx, vertical, hidden, True) ccx_id = CCXLocator.from_course_locator(course.id, str(ccx.id)) # Create forum roles seed_permissions_roles(ccx_id) # Assign administrator forum role to CCX coach assign_role(ccx_id, request.user, FORUM_ROLE_ADMINISTRATOR) url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_id}) # Enroll the coach in the course email_params = get_email_params(course, auto_enroll=True, course_key=ccx_id, display_name=ccx.display_name) enroll_email( course_id=ccx_id, student_email=request.user.email, auto_enroll=True, email_students=True, email_params=email_params, ) assign_staff_role_to_ccx(ccx_id, request.user, course.id) add_master_course_staff_to_ccx(course, ccx_id, ccx.display_name) # using CCX object as sender here. responses = SignalHandler.course_published.send( sender=ccx, course_key=CCXLocator.from_course_locator(course.id, str(ccx.id))) for rec, response in responses: log.info( 'Signal fired when course is published. Receiver: %s. Response: %s', rec, response) return redirect(url)
def student_dashboard(request): """ Provides the LMS dashboard view TODO: This is lms specific and does not belong in common code. Note: To load the all courses set course_limit=None as parameter in GET. If its not None then default course limit will be used that is set in configuration Arguments: request: The request object. Returns: The dashboard response. """ user = request.user #Added by Mahendra if request.is_ajax(): if request.method == 'GET' : if user.is_authenticated: if request.GET.get('disclaimer'): log.info(u'disclaimer %s', request.GET.get('disclaimer')) usr = request.user.id cid = request.GET.get('cid') disclaimer = disclaimer_agreement_status(course_id=cid, user_id=usr, status='1') disclaimer.save() else: usr = request.user.id cid = request.GET.get('cid') view_counter = user_view_counter.objects.filter(course_id=cid, user=usr) if view_counter : update_counter = user_view_counter.objects.filter( course_id=cid, user=usr ).update(counter = F('counter')+1) else: countr = user_view_counter(user_id=usr, course_id=cid,counter=1) countr.save() if not UserProfile.objects.filter(user=user).exists(): return redirect(reverse('account_settings')) platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME) enable_verified_certificates = configuration_helpers.get_value( 'ENABLE_VERIFIED_CERTIFICATES', settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES') ) display_course_modes_on_dashboard = configuration_helpers.get_value( 'DISPLAY_COURSE_MODES_ON_DASHBOARD', settings.FEATURES.get('DISPLAY_COURSE_MODES_ON_DASHBOARD', True) ) activation_email_support_link = configuration_helpers.get_value( 'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK ) or settings.SUPPORT_SITE_LINK hide_dashboard_courses_until_activated = configuration_helpers.get_value( 'HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED', settings.FEATURES.get('HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED', False) ) empty_dashboard_message = configuration_helpers.get_value( 'EMPTY_DASHBOARD_MESSAGE', None ) disable_course_limit = request and 'course_limit' in request.GET course_limit = get_dashboard_course_limit() if not disable_course_limit else None # Get the org whitelist or the org blacklist for the current site site_org_whitelist, site_org_blacklist = get_org_black_and_whitelist_for_site() course_enrollments = list(get_course_enrollments(user, site_org_whitelist, site_org_blacklist, course_limit)) #Added by dev below code to display enrolled courses in course start date order from lms.djangoapps.course_extrainfo.models import course_extrainfo new_enrollments = [] #log.info('course_enrollments--> %s', course_enrollments) for enrollment in course_enrollments: new_enrollments.append(enrollment.course_overview.id) get_type_courses = course_extrainfo.objects.filter(course_id__in=new_enrollments,course_type=1) course_ids = [] for course in get_type_courses: course_ids.append(course.course_id) courseslist = CourseOverview.objects.filter(pk__in=course_ids).order_by('-start') get_type_lectures = course_extrainfo.objects.filter(course_id__in=new_enrollments,course_type=2) lecture_ids = [] for lecture in get_type_lectures: lecture_ids.append(lecture.course_id) lectureslist = CourseOverview.objects.filter(pk__in=lecture_ids).order_by('-start') #code ends here # Get the entitlements for the user and a mapping to all available sessions for that entitlement # If an entitlement has no available sessions, pass through a mock course overview object (course_entitlements, course_entitlement_available_sessions, unfulfilled_entitlement_pseudo_sessions) = get_filtered_course_entitlements( user, site_org_whitelist, site_org_blacklist ) # Record how many courses there are so that we can get a better # understanding of usage patterns on prod. monitoring_utils.accumulate('num_courses', len(course_enrollments)) # Sort the enrollment pairs by the enrollment date course_enrollments.sort(key=lambda x: x.created, reverse=True) # Retrieve the course modes for each course enrolled_course_ids = [enrollment.course_id for enrollment in course_enrollments] __, unexpired_course_modes = CourseMode.all_and_unexpired_modes_for_courses(enrolled_course_ids) course_modes_by_course = { course_id: { mode.slug: mode for mode in modes } for course_id, modes in iteritems(unexpired_course_modes) } # Check to see if the student has recently enrolled in a course. # If so, display a notification message confirming the enrollment. enrollment_message = _create_recent_enrollment_message( course_enrollments, course_modes_by_course ) course_optouts = Optout.objects.filter(user=user).values_list('course_id', flat=True) # Display activation message activate_account_message = '' if not user.is_active: activate_account_message = Text(_( "Check your {email_start}{email}{email_end} inbox for an account activation link from {platform_name}. " "If you need help, contact {link_start}{platform_name} Support{link_end}." )).format( platform_name=platform_name, email_start=HTML("<strong>"), email_end=HTML("</strong>"), email=user.email, link_start=HTML("<a target='_blank' href='{activation_email_support_link}'>").format( activation_email_support_link=activation_email_support_link, ), link_end=HTML("</a>"), ) enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments) # Display a message guiding the user to their Enterprise's Learner Portal if enabled enterprise_learner_portal_enabled_message = get_enterprise_learner_portal_enabled_message(request) recovery_email_message = recovery_email_activation_message = None if is_secondary_email_feature_enabled(): try: pending_email = PendingSecondaryEmailChange.objects.get(user=user) except PendingSecondaryEmailChange.DoesNotExist: try: account_recovery_obj = AccountRecovery.objects.get(user=user) except AccountRecovery.DoesNotExist: recovery_email_message = Text( _( "Add a recovery email to retain access when single-sign on is not available. " "Go to {link_start}your Account Settings{link_end}.") ).format( link_start=HTML("<a href='{account_setting_page}'>").format( account_setting_page=reverse('account_settings'), ), link_end=HTML("</a>") ) else: recovery_email_activation_message = Text( _( "Recovery email is not activated yet. " "Kindly visit your email and follow the instructions to activate it." ) ) # Disable lookup of Enterprise consent_required_course due to ENT-727 # Will re-enable after fixing WL-1315 consent_required_courses = set() enterprise_customer_name = None # Account activation message account_activation_messages = [ message for message in messages.get_messages(request) if 'account-activation' in message.tags ] # Global staff can see what courses encountered an error on their dashboard staff_access = False errored_courses = {} if has_access(user, 'staff', 'global'): # Show any courses that encountered an error on load staff_access = True errored_courses = modulestore().get_errored_courses() show_courseware_links_for = { enrollment.course_id: has_access(request.user, 'load', enrollment.course_overview) for enrollment in course_enrollments } # Find programs associated with course runs being displayed. This information # is passed in the template context to allow rendering of program-related # information on the dashboard. meter = ProgramProgressMeter(request.site, user, enrollments=course_enrollments) ecommerce_service = EcommerceService() inverted_programs = meter.invert_programs() urls, programs_data = {}, {} bundles_on_dashboard_flag = WaffleFlag(experiments_namespace, u'bundles_on_dashboard', __name__) # TODO: Delete this code and the relevant HTML code after testing LEARNER-3072 is complete if bundles_on_dashboard_flag.is_enabled() and inverted_programs and list(inverted_programs.items()): if len(course_enrollments) < 4: for program in inverted_programs.values(): try: program_uuid = program[0]['uuid'] program_data = get_programs(uuid=program_uuid) program_data = ProgramDataExtender(program_data, request.user).extend() skus = program_data.get('skus') checkout_page_url = ecommerce_service.get_checkout_page_url(*skus) program_data['completeProgramURL'] = checkout_page_url + '&bundle=' + program_data.get('uuid') programs_data[program_uuid] = program_data except: # pylint: disable=bare-except pass # Construct a dictionary of course mode information # used to render the course list. We re-use the course modes dict # we loaded earlier to avoid hitting the database. course_mode_info = { enrollment.course_id: complete_course_mode_info( enrollment.course_id, enrollment, modes=course_modes_by_course[enrollment.course_id] ) for enrollment in course_enrollments } # Determine the per-course verification status # This is a dictionary in which the keys are course locators # and the values are one of: # # VERIFY_STATUS_NEED_TO_VERIFY # VERIFY_STATUS_SUBMITTED # VERIFY_STATUS_APPROVED # VERIFY_STATUS_MISSED_DEADLINE # # Each of which correspond to a particular message to display # next to the course on the dashboard. # # If a course is not included in this dictionary, # there is no verification messaging to display. verify_status_by_course = check_verify_status_by_course(user, course_enrollments) cert_statuses = { enrollment.course_id: cert_info(request.user, enrollment.course_overview) for enrollment in course_enrollments } # only show email settings for Mongo course and when bulk email is turned on show_email_settings_for = frozenset( enrollment.course_id for enrollment in course_enrollments if ( is_bulk_email_feature_enabled(enrollment.course_id) ) ) # Verification Attempts # Used to generate the "you must reverify for course x" banner verification_status = IDVerificationService.user_status(user) verification_errors = get_verification_error_reasons_for_display(verification_status['error']) # Gets data for midcourse reverifications, if any are necessary or have failed statuses = ["approved", "denied", "pending", "must_reverify"] reverifications = reverification_info(statuses) enrolled_courses_either_paid = frozenset( enrollment.course_id for enrollment in course_enrollments if enrollment.is_paid_course() ) # If there are *any* denied reverifications that have not been toggled off, # we'll display the banner denied_banner = any(item.display for item in reverifications["denied"]) # get list of courses having pre-requisites yet to be completed courses_having_prerequisites = frozenset( enrollment.course_id for enrollment in course_enrollments if enrollment.course_overview.pre_requisite_courses ) courses_requirements_not_met = get_pre_requisite_courses_not_completed(user, courses_having_prerequisites) site_domain = request.site if 'notlive' in request.GET: if 'viatris' in str(site_domain): redirect_message = _("The webinar you are looking for does not start until {date}.").format( date=request.GET['notlive'] ) else: redirect_message = _("The course you are looking for does not start until {date}.").format( date=request.GET['notlive'] ) elif 'course_closed' in request.GET: redirect_message = _("The course you are looking for is closed for enrollment as of {date}.").format( date=request.GET['course_closed'] ) elif 'access_response_error' in request.GET: # This can be populated in a generalized way with fields from access response errors redirect_message = request.GET['access_response_error'] else: redirect_message = '' valid_verification_statuses = ['approved', 'must_reverify', 'pending', 'expired'] display_sidebar_on_dashboard = verification_status['status'] in valid_verification_statuses and \ verification_status['should_display'] # Filter out any course enrollment course cards that are associated with fulfilled entitlements for entitlement in [e for e in course_entitlements if e.enrollment_course_run is not None]: course_enrollments = [ enr for enr in course_enrollments if entitlement.enrollment_course_run.course_id != enr.course_id ] context = { 'urls': urls, 'programs_data': programs_data, 'enterprise_message': enterprise_message, 'consent_required_courses': consent_required_courses, 'enterprise_customer_name': enterprise_customer_name, 'enrollment_message': enrollment_message, 'redirect_message': Text(redirect_message), 'account_activation_messages': account_activation_messages, 'activate_account_message': activate_account_message, 'course_enrollments': course_enrollments, 'course_entitlements': course_entitlements, 'course_entitlement_available_sessions': course_entitlement_available_sessions, 'unfulfilled_entitlement_pseudo_sessions': unfulfilled_entitlement_pseudo_sessions, 'course_optouts': course_optouts, 'staff_access': staff_access, 'errored_courses': errored_courses, 'show_courseware_links_for': show_courseware_links_for, 'all_course_modes': course_mode_info, 'cert_statuses': cert_statuses, 'credit_statuses': _credit_statuses(user, course_enrollments), 'show_email_settings_for': show_email_settings_for, 'reverifications': reverifications, 'verification_display': verification_status['should_display'], 'verification_status': verification_status['status'], 'verification_expiry': verification_status['verification_expiry'], 'verification_status_by_course': verify_status_by_course, 'verification_errors': verification_errors, 'denied_banner': denied_banner, 'billing_email': settings.PAYMENT_SUPPORT_EMAIL, 'user': user, 'logout_url': reverse('logout'), 'platform_name': platform_name, 'enrolled_courses_either_paid': enrolled_courses_either_paid, 'provider_states': [], 'courses_requirements_not_met': courses_requirements_not_met, 'nav_hidden': True, 'inverted_programs': inverted_programs, 'show_program_listing': ProgramsApiConfig.is_enabled(), 'show_dashboard_tabs': True, 'disable_courseware_js': True, 'display_course_modes_on_dashboard': enable_verified_certificates and display_course_modes_on_dashboard, 'display_sidebar_on_dashboard': display_sidebar_on_dashboard, 'display_sidebar_account_activation_message': not(user.is_active or hide_dashboard_courses_until_activated), 'display_dashboard_courses': (user.is_active or not hide_dashboard_courses_until_activated), 'empty_dashboard_message': empty_dashboard_message, 'recovery_email_message': recovery_email_message, 'recovery_email_activation_message': recovery_email_activation_message, 'enterprise_learner_portal_enabled_message': enterprise_learner_portal_enabled_message, 'show_load_all_courses_link': show_load_all_courses_link(user, course_limit, course_enrollments), # TODO START: clean up as part of REVEM-199 (START) 'course_info': get_dashboard_course_info(user, course_enrollments), # TODO START: clean up as part of REVEM-199 (END) #added by dev 'courseslist':courseslist, 'lectureslist':lectureslist, } context_from_plugins = get_plugins_view_context( ProjectType.LMS, COURSE_DASHBOARD_PLUGIN_VIEW_NAME, context ) context.update(context_from_plugins) course = None context.update( get_experiment_user_metadata_context( course, user, ) ) if ecommerce_service.is_enabled(request.user): context.update({ 'use_ecommerce_payment_flow': True, 'ecommerce_payment_page': ecommerce_service.payment_page_url(), }) # Gather urls for course card resume buttons. resume_button_urls = ['' for entitlement in course_entitlements] for url in get_resume_urls_for_enrollments(user, course_enrollments).values(): resume_button_urls.append(url) # There must be enough urls for dashboard.html. Template creates course # cards for "enrollments + entitlements". context.update({ 'resume_button_urls': resume_button_urls }) return render_to_response('dashboard.html', context)