def put(self):
        """A PUT REST method shared by all unit types."""
        request = transforms.loads(self.request.get('request'))
        key = request.get('key')

        if not self.assert_xsrf_token_or_fail(
                request, 'put-unit', {'key': key}):
            return

        if not CourseOutlineRights.can_edit(self):
            transforms.send_json_response(
                self, 401, 'Access denied.', {'key': key})
            return

        unit = courses.Course(self).find_unit_by_id(key)
        if not unit:
            transforms.send_json_response(
                self, 404, 'Object not found.', {'key': key})
            return

        payload = request.get('payload')
        updated_unit_dict = transforms.json_to_dict(
            transforms.loads(payload), self.SCHEMA_DICT)

        errors = []
        self.apply_updates(unit, updated_unit_dict, errors)
        if not errors:
            course = courses.Course(self)
            assert course.update_unit(unit)
            course.save()
            common_utils.run_hooks(self.POST_SAVE_HOOKS, unit)
            transforms.send_json_response(self, 200, 'Saved.')
        else:
            transforms.send_json_response(self, 412, '\n'.join(errors))
예제 #2
0
    def get(self):
        """Respond to the REST GET verb with the contents of the item."""
        key = self.request.get('key')
        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(self, 401, 'Access denied.',
                                          {'key': key})
            return

        if key:
            item = self.DAO.load(key)
            version = item.dict.get('version')
            if version not in self.SCHEMA_VERSIONS:
                transforms.send_json_response(
                    self, 403, 'Version %s not supported.' % version,
                    {'key': key})
                return
            display_dict = copy.copy(item.dict)
            display_dict['id'] = item.id
            common_utils.run_hooks(self.PRE_LOAD_HOOKS, item, display_dict)
            payload_dict = self.transform_for_editor_hook(display_dict)
        else:
            payload_dict = self.get_default_content()

        transforms.send_json_response(
            self,
            200,
            'Success',
            payload_dict=payload_dict,
            xsrf_token=XsrfTokenManager.create_xsrf_token(self.XSRF_TOKEN))
예제 #3
0
    def put(self):
        """Handles PUT REST verb to save lesson and associated activity."""
        request = transforms.loads(self.request.get('request'))
        key = request.get('key')

        if not self.assert_xsrf_token_or_fail(request, 'lesson-edit',
                                              {'key': key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(self, 401, 'Access denied.',
                                          {'key': key})
            return

        course = courses.Course(self)
        lesson = course.find_lesson_by_id(None, key)
        if not lesson:
            transforms.send_json_response(self, 404, 'Object not found.',
                                          {'key': key})
            return

        payload = request.get('payload')
        updates_dict = transforms.json_to_dict(
            transforms.loads(payload),
            self.get_schema(course, key).get_json_schema_dict())

        lesson.title = updates_dict['title']
        lesson.unit_id = updates_dict['unit_id']
        lesson.scored = (updates_dict['scored'] == 'scored')
        lesson.objectives = updates_dict['objectives']
        lesson.video = updates_dict['video']
        lesson.notes = updates_dict['notes']
        lesson.auto_index = updates_dict['auto_index']
        lesson.activity_title = updates_dict['activity_title']
        lesson.activity_listed = updates_dict['activity_listed']
        lesson.manual_progress = updates_dict['manual_progress']

        activity = updates_dict.get('activity', '').strip()
        errors = []
        if activity:
            if lesson.has_activity:
                course.set_activity_content(lesson, activity, errors=errors)
            else:
                errors.append('Old-style activities are not supported.')
        else:
            lesson.has_activity = False
            fs = self.app_context.fs
            path = fs.impl.physical_to_logical(
                course.get_activity_filename(lesson.unit_id, lesson.lesson_id))
            if fs.isfile(path):
                fs.delete(path)

        if not errors:
            common_utils.run_hooks(self.PRE_SAVE_HOOKS, lesson, updates_dict)
            assert course.update_lesson(lesson)
            course.save()
            common_utils.run_hooks(self.POST_SAVE_HOOKS, lesson)
            transforms.send_json_response(self, 200, 'Saved.')
        else:
            transforms.send_json_response(self, 412, '\n'.join(errors))
예제 #4
0
    def delete_course(cls):
        """Called back repeatedly from deferred queue dispatcher."""
        try:
            kind_name = cls.get_any_undeleted_kind_name()
            if not kind_name:
                # No entity types remain to be deleted from the Datastore for
                # this course (i.e. namespace), so call (in no particular
                # order) callbacks waiting to be informed of course deletion.
                ns_name = namespace_manager.get_namespace()
                common_utils.run_hooks(cls.COURSE_DELETED_HOOKS.itervalues(),
                                       ns_name)
                logging.info(
                    'CourseDeleteHandler found no entity types to delete for '
                    'namespace %s; deletion complete.', ns_name)
                return

            model = Model(kind_name)
            keys = list(
                db.Query(Model(kind_name),
                         keys_only=True).run(batch_size=cls.DELETE_BATCH_SIZE))
            entities.delete(keys)
            logging.info(
                'CourseDeleteHandler deleted %d entities of type %s from '
                'namespace %s', len(keys), kind_name,
                namespace_manager.get_namespace())
            deferred.defer(cls.delete_course)
        except Exception:
            logging.critical(
                'Failed when attempting to delete course for namespace %s',
                namespace_manager.get_namespace())
            common_utils.log_exception_origin()
            raise
    def run(self):
        now = utc.now_as_datetime()
        namespace = namespace_manager.get_namespace()
        app_context = sites.get_app_context_for_namespace(namespace)
        course = courses.Course.get(app_context)
        env = app_context.get_environ()

        tct = triggers.ContentTrigger
        content_acts = tct.act_on_settings(course, env, now)

        tmt = triggers.MilestoneTrigger
        course_acts = tmt.act_on_settings(course, env, now)

        save_settings = content_acts.num_consumed or course_acts.num_consumed
        if save_settings:
            # At least one of the settings['publish'] triggers was consumed
            # or discarded, so save changes to triggers into the settings.
            settings_saved = course.save_settings(env)
        else:
            settings_saved = False

        save_course = content_acts.num_changed or course_acts.num_changed
        if save_course:
            course.save()

        tct.log_acted_on(
            namespace, content_acts, save_course, settings_saved)
        tmt.log_acted_on(
            namespace, course_acts, save_course, settings_saved)

        common_utils.run_hooks(self.RUN_HOOKS.itervalues(), course)
    def put(self):
        """Handles PUT REST verb to save lesson and associated activity."""
        request = transforms.loads(self.request.get('request'))
        key = request.get('key')

        if not self.assert_xsrf_token_or_fail(
                request, 'lesson-edit', {'key': key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(
                self, 401, 'Access denied.', {'key': key})
            return

        course = courses.Course(self)
        lesson = course.find_lesson_by_id(None, key)
        if not lesson:
            transforms.send_json_response(
                self, 404, 'Object not found.', {'key': key})
            return

        payload = request.get('payload')
        updates_dict = transforms.json_to_dict(
            transforms.loads(payload),
            self.get_schema(course, key).get_json_schema_dict())

        lesson.title = updates_dict['title']
        lesson.unit_id = updates_dict['unit_id']
        lesson.scored = (updates_dict['scored'] == 'scored')
        lesson.objectives = updates_dict['objectives']
        lesson.video = updates_dict['video']
        lesson.notes = updates_dict['notes']
        lesson.auto_index = updates_dict['auto_index']
        lesson.activity_title = updates_dict['activity_title']
        lesson.activity_listed = updates_dict['activity_listed']
        lesson.manual_progress = updates_dict['manual_progress']

        activity = updates_dict.get('activity', '').strip()
        errors = []
        if activity:
            if lesson.has_activity:
                course.set_activity_content(lesson, activity, errors=errors)
            else:
                errors.append('Old-style activities are not supported.')
        else:
            lesson.has_activity = False
            fs = self.app_context.fs
            path = fs.impl.physical_to_logical(course.get_activity_filename(
                lesson.unit_id, lesson.lesson_id))
            if fs.isfile(path):
                fs.delete(path)

        if not errors:
            common_utils.run_hooks(self.PRE_SAVE_HOOKS, lesson, updates_dict)
            assert course.update_lesson(lesson)
            course.save()
            common_utils.run_hooks(self.POST_SAVE_HOOKS, lesson)
            transforms.send_json_response(self, 200, 'Saved.')
        else:
            transforms.send_json_response(self, 412, '\n'.join(errors))
예제 #7
0
    def put(self):
        """A PUT REST method shared by all unit types."""
        request = transforms.loads(self.request.get('request'))
        key = request.get('key')

        if not self.assert_xsrf_token_or_fail(request, 'put-unit',
                                              {'key': key}):
            return

        if not CourseOutlineRights.can_edit(self):
            transforms.send_json_response(self, 401, 'Access denied.',
                                          {'key': key})
            return

        unit = courses.Course(self).find_unit_by_id(key)
        if not unit:
            transforms.send_json_response(self, 404, 'Object not found.',
                                          {'key': key})
            return

        payload = request.get('payload')
        updated_unit_dict = transforms.json_to_dict(transforms.loads(payload),
                                                    self.SCHEMA_DICT)

        errors = []
        self.apply_updates(unit, updated_unit_dict, errors)
        if not errors:
            course = courses.Course(self)
            assert course.update_unit(unit)
            course.save()
            common_utils.run_hooks(self.POST_SAVE_HOOKS, unit)
            transforms.send_json_response(self, 200, 'Saved.')
        else:
            transforms.send_json_response(self, 412, '\n'.join(errors))
예제 #8
0
    def run(self):
        now = utc.now_as_datetime()
        namespace = namespace_manager.get_namespace()
        app_context = sites.get_app_context_for_namespace(namespace)
        course = courses.Course.get(app_context)
        env = app_context.get_environ()

        tct = triggers.ContentTrigger
        content_acts = tct.act_on_settings(course, env, now)

        tmt = triggers.MilestoneTrigger
        course_acts = tmt.act_on_settings(course, env, now)

        save_settings = content_acts.num_consumed or course_acts.num_consumed
        if save_settings:
            # At least one of the settings['publish'] triggers was consumed
            # or discarded, so save changes to triggers into the settings.
            settings_saved = course.save_settings(env)
        else:
            settings_saved = False

        save_course = content_acts.num_changed or course_acts.num_changed
        if save_course:
            course.save()

        tct.log_acted_on(namespace, content_acts, save_course, settings_saved)
        tmt.log_acted_on(namespace, course_acts, save_course, settings_saved)

        common_utils.run_hooks(self.RUN_HOOKS.itervalues(), course)
예제 #9
0
    def get(self):
        """Respond to the REST GET verb with the contents of the item."""
        key = self.request.get('key')
        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(
                self, 401, 'Access denied.', {'key': key})
            return

        if key:
            item = self.DAO.load(key)
            if item is None:
                transforms.send_json_response(
                    self, 404, 'Not found.', {'key': key})
                return
            version = item.dict.get('version')
            if version not in self.SCHEMA_VERSIONS:
                transforms.send_json_response(
                    self, 400, 'Version %s not supported.' % version,
                    {'key': key})
                return
            display_dict = copy.copy(item.dict)
            display_dict['id'] = item.id
            common_utils.run_hooks(self.PRE_LOAD_HOOKS, item, display_dict)
            payload_dict = self.transform_for_editor_hook(display_dict)
        elif self.CAN_CREATE:
            payload_dict = self.get_default_content()
        else:
            transforms.send_json_response(
                self, 404, 'Key is required in URL.', {})
            return

        transforms.send_json_response(
            self, 200, 'Success',
            payload_dict=payload_dict,
            xsrf_token=XsrfTokenManager.create_xsrf_token(self.XSRF_TOKEN))
예제 #10
0
    def get(self):
        """Respond to the REST GET verb with the contents of the item."""
        key = self.request.get("key")
        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(self, 401, "Access denied.", {"key": key})
            return

        if key:
            item = self.DAO.load(key)
            version = item.dict.get("version")
            if version not in self.SCHEMA_VERSIONS:
                transforms.send_json_response(self, 403, "Version %s not supported." % version, {"key": key})
                return
            display_dict = copy.copy(item.dict)
            display_dict["id"] = item.id
            common_utils.run_hooks(self.PRE_LOAD_HOOKS, item, display_dict)
            payload_dict = self.transform_for_editor_hook(display_dict)
        else:
            payload_dict = self.get_default_content()

        transforms.send_json_response(
            self,
            200,
            "Success",
            payload_dict=payload_dict,
            xsrf_token=XsrfTokenManager.create_xsrf_token(self.XSRF_TOKEN),
        )
예제 #11
0
    def delete_course(cls):
        """Called back repeatedly from deferred queue dispatcher."""
        try:
            kind_name = cls.get_any_undeleted_kind_name()
            if not kind_name:
                # No entity types remain to be deleted from the Datastore for
                # this course (i.e. namespace), so call (in no particular
                # order) callbacks waiting to be informed of course deletion.
                ns_name = namespace_manager.get_namespace()
                common_utils.run_hooks(
                    cls.COURSE_DELETED_HOOKS.itervalues(), ns_name)
                logging.info(
                    'CourseDeleteHandler found no entity types to delete for '
                    'namespace %s; deletion complete.', ns_name)
                return

            model = Model(kind_name)
            keys = list(db.Query(Model(kind_name), keys_only=True).run(
                batch_size=cls.DELETE_BATCH_SIZE))
            entities.delete(keys)
            logging.info(
                'CourseDeleteHandler deleted %d entities of type %s from '
                'namespace %s', len(keys), kind_name,
                namespace_manager.get_namespace())
            deferred.defer(cls.delete_course)
        except Exception:
            logging.critical(
                'Failed when attempting to delete course for namespace %s',
                namespace_manager.get_namespace())
            common_utils.log_exception_origin()
            raise
    def put(self):
        """Handles PUT REST verb to save lesson and associated activity."""
        request = transforms.loads(self.request.get("request"))
        key = request.get("key")

        if not self.assert_xsrf_token_or_fail(request, "lesson-edit", {"key": key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(self, 401, "Access denied.", {"key": key})
            return

        course = courses.Course(self)
        lesson = course.find_lesson_by_id(None, key)
        if not lesson:
            transforms.send_json_response(self, 404, "Object not found.", {"key": key})
            return

        payload = request.get("payload")
        updates_dict = transforms.json_to_dict(
            transforms.loads(payload), self.get_schema(course, key).get_json_schema_dict()
        )

        lesson.title = updates_dict["title"]
        lesson.unit_id = updates_dict["unit_id"]
        lesson.scored = updates_dict["scored"] == "scored"
        lesson.objectives = updates_dict["objectives"]
        lesson.video = updates_dict["video"]
        lesson.notes = updates_dict["notes"]
        lesson.auto_index = updates_dict["auto_index"]
        lesson.activity_title = updates_dict["activity_title"]
        lesson.activity_listed = updates_dict["activity_listed"]
        lesson.manual_progress = updates_dict["manual_progress"]
        lesson.now_available = not updates_dict["is_draft"]

        activity = updates_dict.get("activity", "").strip()
        errors = []
        if activity:
            if lesson.has_activity:
                course.set_activity_content(lesson, activity, errors=errors)
            else:
                errors.append("Old-style activities are not supported.")
        else:
            lesson.has_activity = False
            fs = self.app_context.fs
            path = fs.impl.physical_to_logical(course.get_activity_filename(lesson.unit_id, lesson.lesson_id))
            if fs.isfile(path):
                fs.delete(path)

        if not errors:
            common_utils.run_hooks(self.PRE_SAVE_HOOKS, lesson, updates_dict)
            assert course.update_lesson(lesson)
            course.save()
            common_utils.run_hooks(self.POST_SAVE_HOOKS, lesson)
            transforms.send_json_response(self, 200, "Saved.")
        else:
            transforms.send_json_response(self, 412, "\n".join(errors))
    def put(self):
        """Store a DTO in the datastore in response to a PUT."""
        request = transforms.loads(self.request.get('request'))
        key = request.get('key')

        if not key and not self.CAN_CREATE:
            transforms.send_json_response(self, 404, 'Key is required in URL.',
                                          {})
            return

        if not self.assert_xsrf_token_or_fail(request, self.XSRF_TOKEN,
                                              {'key': key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(self, 401, 'Access denied.',
                                          {'key': key})
            return

        payload = request.get('payload')
        json_dict = transforms.loads(payload)
        self.sanitize_input_dict(json_dict)

        errors = []
        try:
            python_dict = transforms.json_to_dict(
                json_dict,
                self.get_schema().get_json_schema_dict())

            version = python_dict.get('version')
            if version not in self.SCHEMA_VERSIONS:
                errors.append('Version %s not supported.' % version)
            else:
                python_dict = self.transform_after_editor_hook(python_dict)
                self.validate(python_dict, key, version, errors)
                common_utils.run_hooks(self.VALIDATE_HOOKS, python_dict, key,
                                       version, errors)
        except (TypeError, ValueError) as err:
            errors.append(str(err))
        if errors:
            self.validation_error('\n'.join(
                error.replace('\n', ' ') for error in errors),
                                  key=key)
            return

        item = self.get_and_populate_dto(key, python_dict)
        self.pre_save_hook(item)
        common_utils.run_hooks(self.PRE_SAVE_HOOKS, item, python_dict)
        key_after_save = self.DAO.save(item)
        self.after_save_hook()

        transforms.send_json_response(self,
                                      200,
                                      'Saved.',
                                      payload_dict={'key': key_after_save})
예제 #14
0
    def put(self):
        """Store a DTO in the datastore in response to a PUT."""
        request = transforms.loads(self.request.get('request'))
        key = request.get('key')

        if not key and not self.CAN_CREATE:
            transforms.send_json_response(
                self, 404, 'Key is required in URL.', {})
            return

        if not self.assert_xsrf_token_or_fail(
                request, self.XSRF_TOKEN, {'key': key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(
                self, 401, 'Access denied.', {'key': key})
            return

        payload = request.get('payload')
        json_dict = transforms.loads(payload)
        self.sanitize_input_dict(json_dict)

        errors = []
        try:
            python_dict = transforms.json_to_dict(
                json_dict, self.get_schema().get_json_schema_dict())

            version = python_dict.get('version')
            if version not in self.SCHEMA_VERSIONS:
                errors.append('Version %s not supported.' % version)
            else:
                python_dict = self.transform_after_editor_hook(python_dict)
                self.validate(python_dict, key, version, errors)
                common_utils.run_hooks(
                    self.VALIDATE_HOOKS, python_dict, key, version, errors)
        except (TypeError, ValueError) as err:
            errors.append(str(err))
        if errors:
            self.validation_error('\n'.join(
                error.replace('\n', ' ') for error in errors), key=key)
            return

        item = self.get_and_populate_dto(key, python_dict)
        self.pre_save_hook(item)
        common_utils.run_hooks(self.PRE_SAVE_HOOKS, item, python_dict)
        key_after_save = self.DAO.save(item)
        self.after_save_hook()

        transforms.send_json_response(
            self, 200, 'Saved.', payload_dict={'key': key_after_save})
예제 #15
0
 def _copy_sample_course(self, uid):
     """Make a fresh copy of sample course."""
     src_app_context = sites.get_all_courses('course:/:/:')[0]
     dst_app_context = self._make_new_course(uid, '%s (%s)' % (
         src_app_context.get_title(), os.environ['GCB_PRODUCT_VERSION']))
     errors = []
     dst_course = courses.Course(None, dst_app_context)
     dst_course.import_from(src_app_context, errors)
     dst_course.save()
     if not errors:
         common_utils.run_hooks(
             self.COPY_SAMPLE_COURSE_HOOKS, dst_app_context, errors)
     if errors:
         raise Exception(errors)
     return dst_app_context
예제 #16
0
 def _copy_sample_course(self, uid):
     """Make a fresh copy of sample course."""
     src_app_context = sites.get_all_courses('course:/:/:')[0]
     dst_app_context = self._make_new_course(uid, '%s (%s)' % (
         src_app_context.get_title(), os.environ['GCB_PRODUCT_VERSION']))
     errors = []
     dst_course = courses.Course(None, dst_app_context)
     dst_course.import_from(src_app_context, errors)
     dst_course.save()
     if not errors:
         common_utils.run_hooks(
             self.COPY_SAMPLE_COURSE_HOOKS, dst_app_context, errors)
     if errors:
         raise Exception(errors)
     return dst_app_context
예제 #17
0
    def put(self):
        """Store a DTO in the datastore in response to a PUT."""
        request = transforms.loads(self.request.get("request"))
        key = request.get("key")

        if not self.assert_xsrf_token_or_fail(request, self.XSRF_TOKEN, {"key": key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(self, 401, "Access denied.", {"key": key})
            return

        payload = request.get("payload")
        json_dict = transforms.loads(payload)
        self.sanitize_input_dict(json_dict)

        errors = []
        try:
            python_dict = transforms.json_to_dict(json_dict, self.get_schema().get_json_schema_dict())

            version = python_dict.get("version")
            if version not in self.SCHEMA_VERSIONS:
                errors.append("Version %s not supported." % version)
            else:
                python_dict = self.transform_after_editor_hook(python_dict)
                self.validate(python_dict, key, version, errors)
        except (TypeError, ValueError) as err:
            errors.append(str(err))
        if errors:
            self.validation_error("\n".join(errors), key=key)
            return

        if key:
            item = self.DAO.DTO(key, python_dict)
        else:
            item = self.DAO.DTO(None, python_dict)

        self.pre_save_hook(item)
        common_utils.run_hooks(self.PRE_SAVE_HOOKS, item, python_dict)
        key_after_save = self.DAO.save(item)
        self.after_save_hook()

        transforms.send_json_response(self, 200, "Saved.", payload_dict={"key": key_after_save})
    def delete(self):
        """Delete the Entity in response to REST request."""
        key = self.request.get('key')

        if not self.assert_xsrf_token_or_fail(self.request, self.XSRF_TOKEN,
                                              {'key': key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(self, 401, 'Access denied.',
                                          {'key': key})
            return

        item = self.DAO.load(key)
        if not item:
            transforms.send_json_response(self, 404, 'Not found.',
                                          {'key': key})
            return

        if self.is_deletion_allowed(item):
            self.pre_delete_hook(item)
            common_utils.run_hooks(self.PRE_DELETE_HOOKS, item)
            self.DAO.delete(item)
            transforms.send_json_response(self, 200, 'Deleted.')
예제 #19
0
    def delete(self):
        """Delete the Entity in response to REST request."""
        key = self.request.get('key')

        if not self.assert_xsrf_token_or_fail(
                self.request, self.XSRF_TOKEN, {'key': key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(
                self, 401, 'Access denied.', {'key': key})
            return

        item = self.DAO.load(key)
        if not item:
            transforms.send_json_response(
                self, 404, 'Not found.', {'key': key})
            return

        if self.is_deletion_allowed(item):
            self.pre_delete_hook(item)
            common_utils.run_hooks(self.PRE_DELETE_HOOKS, item)
            self.DAO.delete(item)
            transforms.send_json_response(self, 200, 'Deleted.')
예제 #20
0
class CommonUnitRESTHandler(BaseRESTHandler):
    """A common super class for all unit REST handlers."""

    # These functions are called with an updated unit object whenever a
    # change is saved.
    POST_SAVE_HOOKS = []

    def unit_to_dict(self, unit):
        """Converts a unit to a dictionary representation."""
        return resources_display.UnitTools(self.get_course()).unit_to_dict(unit)

    def apply_updates(self, unit, updated_unit_dict, errors):
        """Applies changes to a unit; modifies unit input argument."""
        resources_display.UnitTools(courses.Course(self)).apply_updates(
            unit, updated_unit_dict, errors)

    def get(self):
        """A GET REST method shared by all unit types."""
        key = self.request.get('key')

        if not CourseOutlineRights.can_view(self):
            transforms.send_json_response(
                self, 401, 'Access denied.', {'key': key})
            return

        unit = courses.Course(self).find_unit_by_id(key)
        if not unit:
            transforms.send_json_response(
                self, 404, 'Object not found.', {'key': key})
            return

        message = ['Success.']
        if self.request.get('is_newly_created'):
            unit_type = verify.UNIT_TYPE_NAMES[unit.type].lower()
            message.append(
                'New %s has been created and saved.' % unit_type)

        transforms.send_json_response(
            self, 200, '\n'.join(message),
            payload_dict=self.unit_to_dict(unit),
            xsrf_token=XsrfTokenManager.create_xsrf_token('put-unit'))

    def put(self):
        """A PUT REST method shared by all unit types."""
        request = transforms.loads(self.request.get('request'))
        key = request.get('key')

        if not self.assert_xsrf_token_or_fail(
                request, 'put-unit', {'key': key}):
            return

        if not CourseOutlineRights.can_edit(self):
            transforms.send_json_response(
                self, 401, 'Access denied.', {'key': key})
            return

        unit = courses.Course(self).find_unit_by_id(key)
        if not unit:
            transforms.send_json_response(
                self, 404, 'Object not found.', {'key': key})
            return

        payload = request.get('payload')
        errors = []

        try:
            updated_unit_dict = transforms.json_to_dict(
                transforms.loads(payload), self.SCHEMA_DICT)
            self.apply_updates(unit, updated_unit_dict, errors)
        except (TypeError, ValueError), ex:
            errors.append(str(ex))

        if not errors:
            course = courses.Course(self)
            assert course.update_unit(unit)
            course.save()
            common_utils.run_hooks(self.POST_SAVE_HOOKS, unit)
            transforms.send_json_response(self, 200, 'Saved.')
        else:
            transforms.send_json_response(self, 412, '\n'.join(errors))
예제 #21
0
 def get_schema(cls):
     question_schema = resources_display.ResourceSAQuestion.get_schema(
         course=None, key=None)
     common_utils.run_hooks(cls.SCHEMA_LOAD_HOOKS, question_schema)
     return question_schema
예제 #22
0
    def put(self):
        """Handles HTTP PUT verb."""
        request = transforms.loads(self.request.get('request'))
        if not self.assert_xsrf_token_or_fail(
                request, self.XSRF_ACTION, {}):
            return

        if not CoursesPropertyRights.can_add():
            transforms.send_json_response(
                self, 401, 'Access denied.')
            return

        payload = request.get('payload')
        json_object = transforms.loads(payload)
        name = json_object.get('name')
        title = json_object.get('title')
        admin_email = json_object.get('admin_email')
        template_course = json_object.get('template_course')

        # Add the new course entry.
        errors = []
        entry = sites.add_new_course_entry(name, title, admin_email, errors)
        if not entry and not errors:
            errors.append('Error adding a new course entry.')
        if errors:
            transforms.send_json_response(self, 412, '\n'.join(errors))
            return

        # We can't expect our new configuration being immediately available due
        # to datastore queries consistency limitations. So we will instantiate
        # our new course here and not use the normal sites.get_all_courses().
        app_context = sites.get_all_courses(entry)[0]

        # Update course with a new title and admin email.
        new_course = courses.Course(None, app_context=app_context)
        if not new_course.init_new_course_settings(title, admin_email):
            transforms.send_json_response(
                self, 412,
                'Added new course entry, but failed to update title and/or '
                'admin email. The course.yaml file already exists and must be '
                'updated manually.')
            return

        if template_course:
            if template_course != 'sample':
                transforms.send_json_response(
                    self, 412,
                    'Unknown template course: %s' % template_course)
                return
            errors = []
            src_app_context = sites.get_all_courses('course:/:/:')[0]
            new_course.import_from(src_app_context, errors)
            new_course.save()
            if not errors:
                common_utils.run_hooks(
                    self.COPY_SAMPLE_COURSE_HOOKS, app_context, errors)
            if errors:
                transforms.send_json_response(self, 412, '\n'.join(errors))
                return

        transforms.send_json_response(
            self, 200, 'Added.', {'entry': entry})
예제 #23
0
    def put(self):
        """Handles HTTP PUT verb."""
        request = transforms.loads(self.request.get('request'))
        if not self.assert_xsrf_token_or_fail(request, self.XSRF_ACTION, {}):
            return

        if not CoursesPropertyRights.can_add():
            self._send_json_error_response(401, 'Access denied.')
            return

        payload = request.get('payload')
        json_object = transforms.loads(payload)
        name = json_object.get('name')
        namespace = 'ns_' + name
        title = json_object.get('title')
        admin_email = json_object.get('admin_email')
        template_course = json_object.get('template_course')

        errors = []
        with common_utils.Namespace(namespace):
            if CourseDeleteHandler.get_any_undeleted_kind_name():
                errors.append(
                    'Unable to add new entry "%s": the corresponding '
                    'namespace "%s" is not empty.  If you removed a '
                    'course with that name in the last few minutes, the '
                    'background cleanup job may still be running.  '
                    'You can use the App Engine Dashboard to manually '
                    'remove all database entities from this namespace.' %
                    (name, namespace))

        # Add the new course entry.
        if not errors:
            entry = sites.add_new_course_entry(name, title, admin_email,
                                               errors)
            if not entry and not errors:
                errors.append('Error adding a new course entry.')
        if errors:
            self._send_json_error_response(412, errors)
            return

        # We can't expect our new configuration being immediately available due
        # to datastore queries consistency limitations. So we will instantiate
        # our new course here and not use the normal sites.get_all_courses().
        app_context = sites.get_all_courses(entry)[0]

        # Update course with a new title and admin email.
        new_course = courses.Course(None, app_context=app_context)
        if not new_course.init_new_course_settings(title, admin_email):
            self._send_json_error_response(
                412,
                'Added new course entry, but failed to update title and/or '
                'admin email. The course.yaml file already exists and must be '
                'updated manually.')
            return

        if template_course:
            if template_course != 'sample':
                self._send_json_error_response(
                    412, 'Unknown template course: %s' % template_course)
                return
            src_app_context = sites.get_all_courses('course:/:/:')[0]
            new_course.import_from(src_app_context, errors)
            new_course.save()
            if not errors:
                common_utils.run_hooks(self.COPY_SAMPLE_COURSE_HOOKS,
                                       app_context, errors)

        if not errors:
            common_utils.run_hooks(self.NEW_COURSE_ADDED_HOOKS.itervalues(),
                                   app_context, errors)

        if errors:
            # Any errors at this point are the result of one or more failed
            # _HOOKS callbacks. It is probably not possible to determine if
            # these are caused by bad server state or instead by bad user
            # input, so return a rather generic 500 HTTP status.
            self._send_json_error_response(500, errors)
        else:
            transforms.send_json_response(self, 200, 'Added.',
                                          {'entry': entry})
예제 #24
0
 def post(self):
     if not self.can_edit():
         return
     common_utils.run_hooks(self.POST_HOOKS, self)
     self.redirect('/modules/admin')
예제 #25
0
 def post(self):
     if not self.can_edit():
         return
     common_utils.run_hooks(self.POST_HOOKS, self)
     self.redirect('/modules/admin')
예제 #26
0
 def post(self):
     if not self.can_edit():
         return
     app_context = super(WelcomeHandler, self).post()
     common_utils.run_hooks(self.POST_HOOKS, app_context, self)
예제 #27
0
 def get_schema(cls):
     schema = drive_models.get_drive_sync_entity_schema()
     common_utils.run_hooks(cls.SCHEMA_LOAD_HOOKS, schema)
     return schema
예제 #28
0
 def post(self):
     if not self.can_edit():
         return
     app_context = super(WelcomeHandler, self).post()
     common_utils.run_hooks(self.POST_HOOKS, app_context, self)
예제 #29
0
 def get_lesson_dict_for(cls, course, lesson):
     lesson_dict = resources_display.ResourceLesson.get_data_dict(
         course, lesson.lesson_id)
     common_utils.run_hooks(cls.PRE_LOAD_HOOKS, lesson, lesson_dict)
     return lesson_dict
예제 #30
0
 def get_schema(cls, course, key):
     lesson_schema = resources_display.ResourceLesson.get_schema(
         course, key)
     common_utils.run_hooks(cls.SCHEMA_LOAD_HOOKS, lesson_schema)
     return lesson_schema
예제 #31
0
    def put(self):
        """Handles HTTP PUT verb."""
        request = transforms.loads(self.request.get('request'))
        if not self.assert_xsrf_token_or_fail(
                request, self.XSRF_ACTION, {}):
            return

        if not CoursesPropertyRights.can_add():
            self._send_json_error_response(401, 'Access denied.')
            return

        payload = request.get('payload')
        json_object = transforms.loads(payload)
        name = json_object.get('name')
        namespace = 'ns_' + name
        title = json_object.get('title')
        admin_email = json_object.get('admin_email')
        template_course = json_object.get('template_course')

        errors = []
        with common_utils.Namespace(namespace):
            if CourseDeleteHandler.get_any_undeleted_kind_name():
                errors.append(
                    'Unable to add new entry "%s": the corresponding '
                    'namespace "%s" is not empty.  If you removed a '
                    'course with that name in the last few minutes, the '
                    'background cleanup job may still be running.  '
                    'You can use the App Engine Dashboard to manually '
                    'remove all database entities from this namespace.' %
                    (name, namespace))

        # Add the new course entry.
        if not errors:
            entry = sites.add_new_course_entry(name, title, admin_email, errors)
            if not entry and not errors:
                errors.append('Error adding a new course entry.')
        if errors:
            self._send_json_error_response(412, errors)
            return

        # We can't expect our new configuration being immediately available due
        # to datastore queries consistency limitations. So we will instantiate
        # our new course here and not use the normal sites.get_all_courses().
        app_context = sites.get_all_courses(entry)[0]

        # Update course with a new title and admin email.
        new_course = courses.Course(None, app_context=app_context)
        if not new_course.init_new_course_settings(title, admin_email):
            self._send_json_error_response(412,
                'Added new course entry, but failed to update title and/or '
                'admin email. The course.yaml file already exists and must be '
                'updated manually.')
            return

        if template_course:
            if template_course != 'sample':
                self._send_json_error_response(
                    412, 'Unknown template course: %s' % template_course)
                return
            src_app_context = sites.get_all_courses('course:/:/:')[0]
            new_course.import_from(src_app_context, errors)
            new_course.save()
            if not errors:
                common_utils.run_hooks(
                    self.COPY_SAMPLE_COURSE_HOOKS, app_context, errors)

        if not errors:
            common_utils.run_hooks(
                self.NEW_COURSE_ADDED_HOOKS.itervalues(), app_context, errors)

        if errors:
            # Any errors at this point are the result of one or more failed
            # _HOOKS callbacks. It is probably not possible to determine if
            # these are caused by bad server state or instead by bad user
            # input, so return a rather generic 500 HTTP status.
            self._send_json_error_response(500, errors)
        else:
            transforms.send_json_response(
                self, 200, 'Added.', {'entry': entry})
 def get_lesson_dict_for(cls, course, lesson):
     lesson_dict = resources_display.ResourceLesson.get_data_dict(
         course, lesson.lesson_id)
     common_utils.run_hooks(cls.PRE_LOAD_HOOKS, lesson, lesson_dict)
     return lesson_dict
 def get_schema(cls, course, key):
     lesson_schema = resources_display.ResourceLesson.get_schema(course, key)
     common_utils.run_hooks(cls.SCHEMA_LOAD_HOOKS, lesson_schema)
     return lesson_schema
예제 #34
0
 def get_schema(cls):
     question_schema = resources_display.ResourceSAQuestion.get_schema(
         course=None, key=None)
     common_utils.run_hooks(cls.SCHEMA_LOAD_HOOKS, question_schema)
     return question_schema
예제 #35
0
 def get_schema(cls):
     schema = drive_models.get_drive_sync_entity_schema()
     common_utils.run_hooks(cls.SCHEMA_LOAD_HOOKS, schema)
     return schema