def get(self): user = users.get_current_user() if not user: self.redirect( users.create_login_url('/modules/admin'), normalize=False) return if not self.can_view(): login_url = users.create_login_url('/modules/admin') node_list = safe_dom.NodeList().append( safe_dom.Element('p').add_text( 'The current user has insufficient rights ' + 'to access this page.')) paragraph = safe_dom.Element('p').add_text('Go to the ') paragraph.append(safe_dom.A(href=login_url).add_text('Login page')) paragraph.add_text( ' to log in as an administrator, or go back to the ') paragraph.append(safe_dom.A(href='/').add_text('Home page')) paragraph.add_text('.') node_list.append(paragraph) self.response.write(node_list.sanitized) return super(WelcomeHandler, self).get()
def get_certificate_table_entry(handler, student, course): # I18N: Title of section on page showing certificates for course completion. title = handler.gettext('Certificate') if student_is_qualified(student, course): nl = safe_dom.NodeList() nl.append( safe_dom.A( CERTIFICATE_HANDLER_PATH ).add_text( # I18N: Label on control to navigate to page showing certificate handler.gettext('Click for certificate')) ).append( safe_dom.Text(' | ') ).append( safe_dom.A( CERTIFICATE_PDF_HANDLER_PATH ).add_text( # I18N: Link for a PDF. handler.gettext('Download PDF')) ) return (title, nl) else: return ( title, # I18N: Text indicating student has not yet completed a course. handler.gettext( 'You have not yet met the course requirements for a ' 'certificate of completion.'))
def get_student_profile_sub_unsub_link(handler, student, unused_course): email = student.email is_unsubscribed = unsubscribe.has_unsubscribed(email) # I18N: Control allowing user to subscribe/unsubscribe from email invitation sub_unsub_title = handler.gettext('Subscribe/Unsubscribe') sub_unsub_message = safe_dom.NodeList() if is_unsubscribed: resubscribe_url = unsubscribe.get_resubscribe_url(handler, email) sub_unsub_message.append( safe_dom.Text( # I18N: Message - user has unsubscribed from email invitations. handler.gettext( 'You are currently unsubscribed from course-related emails.' ))) sub_unsub_message.append( safe_dom.A(resubscribe_url).add_text( # I18N: Control allowing user to re-subscribe to email invitations. handler.gettext('Click here to re-subscribe.'))) else: unsubscribe_url = unsubscribe.get_unsubscribe_url(handler, email) sub_unsub_message.append( safe_dom.Text( # I18N: Text indicating user has opted in to email invitations. handler.gettext( 'You are currently receiving course-related emails. '))) sub_unsub_message.append( safe_dom.A(unsubscribe_url).add_text( # I18N: Control allowing user to unsubscribe from email invitations. handler.gettext('Click here to unsubscribe.'))) return (sub_unsub_title, sub_unsub_message)
def get(self): action = self.request.get('action') if action: destination = '%s?action=%s' % (self.URL, action) else: destination = self.URL user = users.get_current_user() login_url = users.create_login_url(destination) if not user: self.redirect(login_url, normalize=False) return if not can_view_admin_action(action): node_list = safe_dom.NodeList().append( safe_dom.Element('p').add_text( 'The current user has insufficient rights ' + 'to access this page.')) paragraph = safe_dom.Element('p').add_text('Go to the ') paragraph.append(safe_dom.A(href=login_url).add_text('Login page')) paragraph.add_text( ' to log in as an administrator, or go back to the ') paragraph.append(safe_dom.A(href='/').add_text('Home page')) paragraph.add_text('.') node_list.append(paragraph) self.response.write(node_list.sanitized) self.response.set_status(403) return if action not in self._custom_get_actions: config.Registry.get_overrides(force_update=True) super(GlobalAdminHandler, self).get() return result = self._custom_get_actions[action].handler(self) if result is None: return # The following code handles pages for actions that do not write out # their responses. template_values = { 'page_title': self.format_title(self.get_nav_title(action)), } if isinstance(result, dict): template_values.update(result) else: template_values['main_content'] = result self.render_page(template_values) return
def create_edit_button(edit_url): return safe_dom.A( href=edit_url, className='icon md-mode-edit row-hover', title='Edit', alt='Edit', )
def add_delete_data_footer(cls, app_context): user = users.get_current_user() if not user: return [] # No need for link if user has no PII in the course. Here, we are # using the presence of a Student record as a marker for that, and # that's reasonable. If there is no Student record, then either # there is no PII, or deletion is already in progress, and # re-requesting deletion would be pointless. student = models.Student.get_by_user(user) if not student or student.is_transient: return [] if student.is_enrolled: link = 'student/unenroll' else: link = DataRemovalConfirmationHandler.URL.lstrip('/') # I18N: Gives a link on the footer of the page to permit the user # to delete their personal data. This link only appears for users # who are currently enrolled in the course or who were enrolled # but still have personal data present in the course. text = app_context.gettext('Delete My Data') return [ safe_dom.Element('li').add_child(safe_dom.A(link).add_text(text))]
def get_pipeline_link(xsrf, app_context, generator_class, job): ret = safe_dom.NodeList() if (not issubclass(generator_class, jobs.MapReduceJob) or # Don't give access to the pipeline details UI unless someone # has actively intended to provide access. The UI allows you to # kill jobs, and we don't want naive users stumbling around in # there without adult supervision. not mapreduce_module.GCB_ENABLE_MAPREDUCE_DETAIL_ACCESS.value or # Status URL may not be available immediately after job is launched; # pipeline setup is done w/ 'yield', and happens a bit later. not job or not jobs.MapReduceJob.has_status_url(job)): return ret if job.has_finished: link_text = 'View completed job run details' else: link_text = 'Check status of job' status_url = jobs.MapReduceJob.get_status_url( job, app_context.get_namespace_name(), xsrf.create_xsrf_token(mapreduce_module.XSRF_ACTION_NAME)) ret.append(safe_dom.Text(' ')) ret.append(safe_dom.A(status_url, target='_blank').add_text(link_text)) return ret
def create_launch_button(url, active=True): if active: return safe_dom.A( href=url, className='icon row-hover material-icons', ).add_text('open_in_new') else: return safe_dom.Element('div', className='icon inactive')
def _create_clone_button(question_id): return safe_dom.A( href='#', className='icon md md-content-copy', title='Clone', alt='Clone', data_key=str(question_id) )
def create_edit_button(edit_url, editable=True): if editable: return safe_dom.A( href=edit_url, className='icon md-mode-edit md row-hover', title='Edit', alt='Edit', ) else: return safe_dom.Element('div', className='icon inactive')
def _make_welcome_form_content(): """Add content to welcome page to get user's consent for stat collection.""" if messaging.is_disabled(): return None checkbox = safe_dom.Element( 'input' ).set_attribute( 'type', 'checkbox' ).set_attribute( 'name', USAGE_REPORTING_CONSENT_CHECKBOX_NAME ).set_attribute( 'value', USAGE_REPORTING_CONSENT_CHECKBOX_VALUE ) if config.REPORT_ALLOWED.value or not config.is_consent_set(): checkbox.set_attribute('checked', 'checked') return safe_dom.Element( 'div' ).set_attribute( 'style', 'width: 60%; margin: 0 auto; ' ).append( safe_dom.Element( 'div' ).set_attribute( 'style', 'float: left; width: 10%; ' ).add_child(checkbox) ).append( safe_dom.Element( 'div' ).set_attribute( 'style', 'float: left; width: 90%; text-align: left' ).add_text( 'I agree that Google may collect information about this ' 'deployment of Course Builder to help improve Google\'s ' 'products and services and for research purposes. ' 'Google will maintain this data in acccordance with ' ).add_child( safe_dom.A( 'http://www.google.com/policies/privacy/' ).add_text( 'Google\'s privacy policy' ) ).add_text( ' and will not associate the data it collects with ' 'this course or a user. Your response to this question ' 'will be sent to Google.' ) ).append( safe_dom.Element( 'div' ).set_attribute( 'style', 'clear: both; ' ) )
def _list_labels(handler): """Prepare a list of labels for use on the Assets page.""" output = safe_dom.NodeList() if not handler.app_context.is_editable_fs(): return output output.append( safe_dom.A('dashboard?action=add_label', className='gcb-button gcb-pull-right' ).add_text('Add Label') ).append( safe_dom.Element( 'div', style='clear: both; padding-top: 2px;' ) ) output.append( safe_dom.Element('h3').add_text('Labels') ) labels = models.LabelDAO.get_all() if labels: all_labels_ul = safe_dom.Element('ul') output.append(all_labels_ul) for label_type in sorted( models.LabelDTO.LABEL_TYPES, lambda a, b: cmp(a.menu_order, b.menu_order)): type_li = safe_dom.Element('li').add_text(label_type.title) all_labels_ul.add_child(type_li) labels_of_type_ul = safe_dom.Element('ul') type_li.add_child(labels_of_type_ul) for label in sorted( labels, lambda a, b: cmp(a.title, b.title)): if label.type == label_type.type: li = safe_dom.Element('li') labels_of_type_ul.add_child(li) li.add_text( label.title ).add_attribute( title='id: %s, type: %s' % (label.id, label_type)) if label_type not in ( models.LabelDTO.SYSTEM_EDITABLE_LABEL_TYPES): li.add_child( dashboard_utils.create_edit_button( 'dashboard?action=edit_label&key=%s' % label.id, ).add_attribute( id='label_%s' % label.title)) else: output.append(safe_dom.Element('blockquote').add_text('< none >')) return output
def _add_pipeline_link(self, job, template_values, message): status_url = jobs.MapReduceJob.get_status_url( # app_context is forcibly injected into instances from # modules.dashboard.get_analytics() # pylint: disable=undefined-variable job, self.app_context.get_namespace_name(), utils.XsrfTokenManager.create_xsrf_token( mapreduce_module.XSRF_ACTION_NAME)) update_message = safe_dom.NodeList() update_message.append(template_values['update_message']) update_message.append(safe_dom.Text(' ')) update_message.append( safe_dom.A(status_url, target='_blank').add_text(message)) template_values['update_message'] = update_message
def get_certificate_table_entry(unused_handler, student, course): # I18N: Title of section on page showing certificates for course completion. title = gettext.gettext('Certificate') if student_is_qualified(student, course): link = safe_dom.A(CERTIFICATE_HANDLER_PATH).add_text( # I18N: Label on control to navigate to page showing certificate. gettext.gettext('Click for certificate')) return (title, link) else: return ( title, # I18N: Text indicating student has not yet completed a course. gettext.gettext( 'You have not yet met the course requirements for a ' 'certificate of completion.'))
def get_course_picker(self, destination=None): destination = destination or '/dashboard' action = self.request.get('action') or self._default_get_action # disable picker if we are on the well known page; we dont want picked # on pages where edits or creation of new object can get triggered safe_action = action and action in [ a for a, _ in self.get_nav_mappings() ] + ['admin'] tab = self.request.get('tab') if action in self.get_actions: tab_group = tabs.Registry.get_tab_group(action) if tab_group and tab in tab_group: tab = '&tab=%s' % tab else: tab = '' destination = '%s?action=%s%s' % (destination, action, tab) current_course = sites.get_course_for_current_request() options = [] for course in sorted(sites.get_all_courses()): with Namespace(course.namespace): if self.current_user_has_access(course): url = (course.canonicalize_url(destination) if safe_action else 'javascript:void(0)') title = '%s (%s)' % (course.get_title(), course.get_slug()) option = safe_dom.Element('li') link = safe_dom.A(url).add_text(title) if current_course == course: link.set_attribute('class', 'selected') option.add_child(link) options.append((course.get_title(), option)) picker_class_name = 'hidden' if not safe_action: picker_class_name += ' disabled' picker = safe_dom.Element('ol', id='gcb-course-picker-menu', className=picker_class_name) for title, option in sorted(options, key=lambda item: item[0].lower()): picker.append(option) return picker
def _get_top_nav(self, in_action, in_tab): nav_bars = [] nav = safe_dom.NodeList() nav.append( safe_dom.Element('a', href=self.URL, className='selected').add_text('Site Admin')) nav.append( safe_dom.Element( 'a', href='https://code.google.com/p/course-builder/wiki/Dashboard', target='_blank').add_text('Help')) nav.append( safe_dom.Element( 'a', href=( 'https://groups.google.com/forum/?fromgroups#!categories/' 'course-builder-forum/general-troubleshooting'), target='_blank').add_text('Support')) nav_bars.append(nav) tab_name = in_tab or self.request.get('tab') or self.DEFAULT_TAB sub_nav = safe_dom.NodeList() tab_group = tabs.Registry.get_tab_group(self.default_action) for tab in tab_group: if tab.contents: href = '%s?tab=%s' % (self.LINK_URL, tab.name) else: href = tab.href target = tab.target or '_self' sub_nav.append( safe_dom.A( href, className=('selected' if tab.name == tab_name else ''), target=target).add_text(tab.title)) nav_bars.append(sub_nav) return nav_bars
def get_student_profile_invitation_link(handler, unused_student, unused_course): env = handler.app_context.get_environ() email_env = env['course'].get(INVITATION_EMAIL_KEY, {}) if not email_env.get('enabled'): return (None, None) # I18N: Title encouraging user to invite friends to join a course invitation_title = handler.gettext('Invite Friends') if InvitationEmail.is_available(handler): invitation_link = safe_dom.A( InvitationHandler.URL # I18N: Label on control asking user to invite friends to join. ).add_text( handler.gettext('Click to send invitations to family and friends')) else: # I18N: Inviting friends to join a course is not currently enabled. invitation_link = safe_dom.Text( handler.gettext('Invitations not currently available')) return (invitation_title, invitation_link)
MODULE_NAME = 'Map/Reduce' XSRF_ACTION_NAME = 'view-mapreduce-ui' MAX_MAPREDUCE_METADATA_RETENTION_DAYS = 3 GCB_ENABLE_MAPREDUCE_DETAIL_ACCESS = ConfigProperty( 'gcb_enable_mapreduce_detail_access', bool, safe_dom.NodeList().append( safe_dom.Element('p').add_text(""" Enables access to status pages showing details of progress for individual map/reduce jobs as they run. These pages can be used to cancel jobs or sub-jobs. This is a benefit if you have launched a huge job that is consuming too many resources, but a hazard for naive users.""")).append( safe_dom.Element('p').add_child( safe_dom.A('/mapreduce/ui/pipeline/list', target='_blank').add_text(""" See an example page (with this control enabled)"""))), False, multiline=False, validator=None) def authorization_wrapper(self, *args, **kwargs): # developers.google.com/appengine/docs/python/taskqueue/overview-push # promises that this header cannot be set by external callers. If this # is present, we can be certain that the request is internal and from # the task queue worker. (This is belt-and-suspenders with the admin # restriction on /mapreduce/worker*) if 'X-AppEngine-TaskName' not in self.request.headers: self.response.out.write('Forbidden') self.response.set_status(403)
def _get_top_nav(self, in_action, in_tab): current_action = in_action or self.request.get( 'action') or self.default_action nav_bars = [] nav = safe_dom.NodeList() for action, title in self.get_nav_mappings(): if not self.can_view(action): continue class_name = 'selected' if action == current_action else '' action_href = 'dashboard?action=%s' % action nav.append( safe_dom.Element('a', href=action_href, className=class_name).add_text(title)) if roles.Roles.is_super_admin(): nav.append( safe_dom.Element( 'a', href='admin?action=admin', className=('selected' if current_action == 'admin' else '')).add_text('Site Admin')) nav.append( safe_dom.Element( 'a', href='https://code.google.com/p/course-builder/wiki/Dashboard', target='_blank').add_text('Help')) nav.append( safe_dom.Element( 'a', href=( 'https://groups.google.com/forum/?fromgroups#!categories/' 'course-builder-forum/general-troubleshooting'), target='_blank').add_text('Support')) nav_bars.append(nav) tab_group = tabs.Registry.get_tab_group(current_action) if tab_group: if current_action == 'assets': exclude_tabs = [] course = self.get_course() if courses.has_only_new_style_assessments(course): exclude_tabs.append('Assessments') if courses.has_only_new_style_activities(course): exclude_tabs.append('Activities') tab_group = [ t for t in tab_group if t.title not in exclude_tabs ] tab_name = (in_tab or self.request.get('tab') or self.default_subtab_action[current_action] or tab_group[0].name) sub_nav = safe_dom.NodeList() for tab in tab_group: href = tab.href or 'dashboard?action=%s&tab=%s' % ( current_action, tab.name) target = tab.target or '_self' sub_nav.append( safe_dom.A( href, className=('selected' if tab.name == tab_name else ''), target=target).add_text(tab.title)) nav_bars.append(sub_nav) return nav_bars
def _list_and_format_file_list( handler, title, subfolder, tab_name, links=False, upload=False, prefix=None, caption_if_empty='< none >', edit_url_template=None, merge_local_files=False, sub_title=None, all_paths=None): """Walks files in folders and renders their names in a section.""" # keep a list of files without merging unmerged_files = {} if merge_local_files: unmerged_files = dashboard_utils.list_files( handler, subfolder, merge_local_files=False, all_paths=all_paths) items = safe_dom.NodeList() count = 0 for filename in dashboard_utils.list_files( handler, subfolder, merge_local_files=merge_local_files, all_paths=all_paths): if prefix and not filename.startswith(prefix): continue # make a <li> item li = safe_dom.Element('li') if links: url = urllib.quote(filename) li.add_child(safe_dom.Element( 'a', href=url).add_text(filename)) else: li.add_text(filename) # add actions if available if (edit_url_template and handler.app_context.fs.impl.is_read_write()): li.add_child(safe_dom.Entity(' ')) edit_url = edit_url_template % ( tab_name, urllib.quote(filename)) # show [overridden] + edit button if override exists if (filename in unmerged_files) or (not merge_local_files): li.add_text('[Overridden]').add_child( dashboard_utils.create_edit_button(edit_url)) # show an [override] link otherwise else: li.add_child(safe_dom.A(edit_url).add_text('[Override]')) count += 1 items.append(li) output = safe_dom.NodeList() if handler.app_context.is_editable_fs() and upload: output.append( safe_dom.Element( 'a', className='gcb-button gcb-pull-right', href='dashboard?%s' % urllib.urlencode( {'action': 'manage_asset', 'tab': tab_name, 'key': subfolder}) ).add_text( 'Upload to ' + subfolder.lstrip('/').rstrip('/')) ).append( safe_dom.Element( 'div', style='clear: both; padding-top: 2px;' ) ) if title: h3 = safe_dom.Element('h3') if count: h3.add_text('%s (%s)' % (title, count)) else: h3.add_text(title) output.append(h3) if sub_title: output.append(safe_dom.Element('blockquote').add_text(sub_title)) if items: output.append(safe_dom.Element('ol').add_children(items)) else: if caption_if_empty: output.append( safe_dom.Element('blockquote').add_text(caption_if_empty)) return output