def reorder(self, id, direction, **kwargs):
        """Reorder a PlayerPref.

        :param id: Player ID.
        :type id: ``int``
        :param direction: ``"up"`` for higher priority, ``"down"`` for
            lower priority
        :type direction: ``unicode``
        :returns: Redirect back to :meth:`index` after success.
        """
        if direction == 'up':
            offset = -1
        elif direction == 'down':
            offset = 1
        else:
            return

        player1 = fetch_row(PlayerPrefs, id)
        new_priority = player1.priority + offset
        try:
            player2 = fetch_row(PlayerPrefs, priority=new_priority)
            player2.priority = player1.priority
            player1.priority = new_priority
        except HTTPException, e:
            if e.code != 404:
                raise
Пример #2
0
    def save(self, id, email_address, display_name, login_details,
             delete=None, **kwargs):
        """Save changes or create a new :class:`~mediacore.model.auth.User` instance.

        :param id: User ID. If ``"new"`` a new user is created.
        :type id: ``int`` or ``"new"``
        :returns: Redirect back to :meth:`index` after successful save.

        """
        user = fetch_row(User, id)

        if delete:
            DBSession.delete(user)
            redirect(action='index', id=None)

        user.display_name = display_name
        user.email_address = email_address
        user.user_name = login_details['user_name']

        password = login_details['password']
        if password is not None and password != '':
            user.password = password

        if login_details['group']:
            group = fetch_row(Group, login_details['group'])
            user.groups = [group]
        else:
            user.groups = []

        DBSession.add(user)
        DBSession.flush()
        redirect(action='index', id=None)
Пример #3
0
    def save(self, id, delete=None, **kwargs):
        """Save changes or create a category.

        See :class:`~mediacore.forms.admin.settings.categories.CategoryForm` for POST vars.

        :param id: Category ID
        :param delete: If true the category is to be deleted rather than saved.
        :type delete: bool
        :rtype: JSON dict
        :returns:
            success
                bool

        """
        if tmpl_context.form_errors:
            if request.is_xhr:
                return dict(success=False, errors=tmpl_context.form_errors)
            else:
                # TODO: Add error reporting for users with JS disabled?
                return redirect(action="edit")

        cat = fetch_row(Category, id)

        if delete:
            DBSession.delete(cat)
            data = dict(success=True, id=cat.id, parent_options=unicode(category_form.c["parent_id"].display()))
        else:
            cat.name = kwargs["name"]
            cat.slug = get_available_slug(Category, kwargs["slug"], cat)

            if kwargs["parent_id"]:
                parent = fetch_row(Category, kwargs["parent_id"])
                if parent is not cat and cat not in parent.ancestors():
                    cat.parent = parent
            else:
                cat.parent = None

            DBSession.add(cat)
            DBSession.flush()

            data = dict(
                success=True,
                id=cat.id,
                name=cat.name,
                slug=cat.slug,
                parent_id=cat.parent_id,
                parent_options=unicode(category_form.c["parent_id"].display()),
                depth=cat.depth(),
                row=unicode(
                    category_row_form.display(
                        action=url_for(id=cat.id), category=cat, depth=cat.depth(), first_child=True
                    )
                ),
            )

        if request.is_xhr:
            return data
        else:
            redirect(action="index", id=None)
Пример #4
0
    def index(self, page=1, search=None, filter=None, podcast=None, category=None, tag=None, **kwargs):
        """List media with pagination and filtering.

        :param page: Page number, defaults to 1.
        :type page: int
        :param search: Optional search term to filter by
        :type search: unicode or None
        :param podcast_filter: Optional podcast to filter by
        :type podcast_filter: int or None
        :rtype: dict
        :returns:
            media
                The list of :class:`~mediacore.model.media.Media` instances
                for this page.
            search
                The given search term, if any
            search_form
                The :class:`~mediacore.forms.admin.SearchForm` instance
            podcast
                The podcast object for rendering if filtering by podcast.

        """
        media = Media.query.options(orm.undefer("comment_count_published"))

        if search:
            media = media.admin_search(search)
        else:
            media = media.order_by_status().order_by(Media.publish_on.desc(), Media.modified_on.desc())

        if not filter:
            pass
        elif filter == "unreviewed":
            media = media.reviewed(False)
        elif filter == "unencoded":
            media = media.reviewed().encoded(False)
        elif filter == "drafts":
            media = media.drafts()
        elif filter == "published":
            media = media.published()

        if category:
            category = fetch_row(Category, slug=category)
            media = media.filter(Media.categories.contains(category))
        if tag:
            tag = fetch_row(Tag, slug=tag)
            media = media.filter(Media.tags.contains(tag))
        if podcast:
            podcast = fetch_row(Podcast, slug=podcast)
            media = media.filter(Media.podcast == podcast)

        return dict(
            media=media,
            search=search,
            search_form=search_form,
            media_filter=filter,
            category=category,
            tag=tag,
            podcast=podcast,
        )
Пример #5
0
    def edit_file(self, id, file_id, file_type=None, delete=None, **kwargs):
        """Save action for the :class:`~mediacore.forms.admin.media.EditFileForm`.

        Changes or delets a :class:`~mediacore.model.media.MediaFile`.

        :param id: Media ID
        :type id: :class:`int`
        :rtype: JSON dict
        :returns:
            success
                bool
            message
                Error message, if unsuccessful
            status_form
                Rendered XHTML for the status form, updated to reflect the
                changes made.

        """
        media = fetch_row(Media, id)
        data = dict(success=False)

        try:
            file = [file for file in media.files if file.id == int(file_id)][0]
        except IndexError:
            data['message'] = 'File no longer exists.'

        if file_type:
            file.type = file_type
            DBSession.add(file)
            data['success'] = True
        elif delete:
            file_path = file.file_path
            DBSession.delete(file)
            transaction.commit()
            if file_path:
                helpers.delete_files([file_path], 'media')
            media = fetch_row(Media, id)
            data['success'] = True
        else:
            data['message'] = 'No action to perform.'

        if data['success']:
            data['file_type'] = file.type
            media.update_type()
            media.update_status()
            DBSession.add(media)
            DBSession.flush()

            # Return the rendered widget for injection
            status_form_xhtml = unicode(update_status_form.display(
                action=url_for(action='update_status'), media=media))
            data['status_form'] = status_form_xhtml
        return data
Пример #6
0
    def latest(self, type=None, podcast=None, ignore=None,
               topic=None, tag=None, **kwargs):
        """Expose basic info of the latest media object.

        .. todo:: Work this into a more general, documented API scheme.

        :param type: ``audio``, ``video``, or ``None`` for either
        :param podcast: A :attr:`mediacore.model.podcasts.Podcast.slug`
            or empty string.
        :param topic: A topic slug
        :param ignore: A slug (or slugs, separated by whitespace)
            to always exclude from results. This allows us to fetch
            two DIFFERENT results when calling this action twice.
        :rtype: JSON dict

        """
        media_query = self._published_media_query

        if type:
            media_query = media_query.filter(Media.type == type)

        # Filter by podcast, if podcast slug provided
        if podcast != None:
            if podcast == '':
                media_query = media_query\
                    .filter(Media.podcast_id == None)
            else:
                podcast = fetch_row(Podcast, slug=podcast)
                media_query = media_query\
                    .filter(Media.podcast_id == podcast.id)

        # Filter by topic, if topic slug provided
        if topic:
            topic = fetch_row(Topic, slug=topic)
            media_query = media_query\
                .filter(Media.topics.contains(topic))

        # Filter by tag, if tag slug provided
        if tag:
            tag = fetch_row(Tag, slug=tag)
            media_query = media_query\
                .filter(Media.tags.contains(tag))

        # Filter out a media item we don't like
        if ignore:
            ignore = ignore.split(' ')
            media_query = media_query.filter(sql.not_(Media.slug.in_(ignore)))

        # get the actual object (hope there is one!)
        media = media_query.first()

        return self._jsonify(media)
Пример #7
0
    def panda_update(self, media_id=None, file_id=None, video_id=None, **kwargs):
        if file_id:
            media_file = fetch_row(MediaFile, file_id)
            media_files = [media_file]
        elif media_id:
            media = fetch_row(Media, media_id)
            media_files = media.files

        storage = DBSession.query(PandaStorage).first()

        for media_file in media_files:
            storage.panda_helper().video_status_update(media_file, video_id)

        redirect(controller='/admin/media', action='edit', id=media_id)
    def edit(self, id, **kwargs):
        """Display the :class:`~mediacore.forms.admin.groups.GroupForm` for editing or adding.

        :param id: Group ID
        :type id: ``int`` or ``"new"``
        :rtype: dict
        :returns:
            user
                The :class:`~mediacore.model.auth.Group` instance we're editing.
            user_form
                The :class:`~mediacore.forms.admin.groups.GroupForm` instance.
            user_action
                ``str`` form submit url
            group_values
                ``dict`` form values

        """
        group = fetch_row(Group, id)

        if tmpl_context.action == 'save' or id == 'new':
            # Use the values from error_handler or GET for new groups
            group_values = kwargs
        else:
            group_values = dict(
                display_name = group.display_name,
                group_name = group.group_name,
            )

        return dict(
            group = group,
            group_form = group_form,
            group_action = url_for(action='save'),
            group_values = group_values,
        )
Пример #9
0
    def serve(self, id, slug, container, **kwargs):
        """Serve a :class:`~mediacore.model.media.MediaFile` binary.

        :param id: File ID
        :type id: ``int``
        :param slug: The media :attr:`~mediacore.model.media.Media.slug`
        :type slug: The file :attr:`~mediacore.model.media.MediaFile.container`
        :raises webob.exc.HTTPNotFound: If no file exists for the given params.
        :raises webob.exc.HTTPNotAcceptable: If an Accept header field
            is present, and if the mimetype of the requested file doesn't
            match, then a 406 (not acceptable) response is returned.

        """
        media = fetch_row(Media, slug=slug)

        for file in media.files:
            if file.id == id and file.container == container:
                # Catch external redirects in case they aren't linked to directly
                if file.url:
                    redirect(file.url.encode('utf-8'))

                # Ensure that the clients request allows for files of this container
                mimetype = mimeparse.best_match([file.mimetype],
                    request.environ.get('HTTP_ACCEPT', '*/*'))
                if mimetype == '':
                    raise webob.exc.HTTPNotAcceptable() # 406

                response.headers['Content-Type'] = mimetype
                response.headers['Content-Disposition'] = \
                    'attachment;filename="%s"' % file.display_name.encode('utf-8')
                return open(file.file_path, 'rb').read()
        else:
            raise webob.exc.HTTPNotFound()
Пример #10
0
    def feed(self, slug, **kwargs):
        """Serve the feed as RSS 2.0.

        If :attr:`~mediacore.model.podcasts.Podcast.feedburner_url` is
        specified for this podcast, we redirect there.

        :param slug: A :attr:`~mediacore.model.podcasts.Podcast.slug`
        :param page: Page number, defaults to 1.
        :type page: int
        :rtype: dict
        :returns:
            podcast
                A :class:`~mediacore.model.podcasts.Podcast` instance.
            episodes
                A list of :class:`~mediacore.model.media.Media` instances
                that belong to the ``podcast``.
            podcasts
                A list of all the other podcasts

        """
        podcast = fetch_row(Podcast, slug=slug)
        episodes = self._filter(podcast.media).order_by(Media.publish_on.desc())

        podcasts = DBSession.query(Podcast).options(orm.undefer("published_media_count")).all()

        return dict(podcast=podcast, episodes=episodes, podcasts=podcasts)
Пример #11
0
 def _import_video(self, entry):
     player_url = self._player_url_from_entry(entry)
     if not player_url:
         log.debug('Video Feed Error: No player URL? %s' % entry)
         return None
     if self._has_media_file_for(player_url):
         return None
     
     media = fetch_row(Media, u'new')
     media.author = Author(self.user.display_name, self.user.email_address)
     media.reviewed = True
     media.title = unicode(entry.media.title.text, "utf-8")
     if entry.media.description.text:
         encoded_description = unicode(entry.media.description.text, "utf-8")
         media.description = clean_xhtml(encoded_description)
     media.slug = get_available_slug(Media, media.title, media)
     
     if self.tags:
         media.set_tags(unicode(self.tags))
     if self.categories:
         media.set_categories(self.categories)
     try:
         media_file = add_new_media_file(media, url=player_url)
     except StorageError, e:
         log.debug('Video Feed Error: Error storing video: %s at %s' \
             % (e.message, player_url))
         return None
    def test_submit(self):
        fields, files = self._valid_values('testing mp3 upload')
        index_url = url(controller='upload', action='index')
        submit_url = url(controller='upload', action='submit')
        success_url = url(controller='upload', action='success')

        index_response = self.app.get(index_url, status=200)

        # Ensure that the form has the correct action and fields
        form = index_response.forms['upload-form']
        for x in fields:
            form[x] = fields[x]
        assert form.action == submit_url

        # Submit the form with a regular POST request anyway, because
        # webtest.Form objects can't handle file uploads.
        submit_response = self.app.post(submit_url, params=fields, upload_files=files)
        assert submit_response.status_int == 302
        assert submit_response.location == 'http://localhost%s' % success_url

        # Ensure the media item and file were  created properly.
        media = fetch_row(Media, slug=u'testing-mp3-upload')
        assert len(media.files) == 1
        assert media.files[0].container == 'mp3'
        assert media.description == "<p>actually just testing an mp3 upload.</p>"
Пример #13
0
    def view(self, slug, page=1, show='latest', **kwargs):
        """View a podcast and the media that belongs to it.

        :param slug: A :attr:`~mediacore.model.podcasts.Podcast.slug`
        :param page: Page number, defaults to 1.
        :type page: int
        :rtype: dict
        :returns:
            podcast
                A :class:`~mediacore.model.podcasts.Podcast` instance.
            episodes
                A list of :class:`~mediacore.model.media.Media` instances
                that belong to the ``podcast``.
            podcasts
                A list of all the other podcasts

        """
        podcast = fetch_row(Podcast, slug=slug)
        episodes = podcast.media.published()

        episodes, show = helpers.filter_library_controls(episodes, show)

        return dict(
            podcast = podcast,
            episodes = episodes,
            result_count = episodes.count(),
            show = show,
        )
Пример #14
0
    def feed(self, slug, **kwargs):
        """Serve the feed as RSS 2.0.

        If :attr:`~mediacore.model.podcasts.Podcast.feedburner_url` is
        specified for this podcast, we redirect there if the useragent
        does not contain 'feedburner', as described here:
        http://www.google.com/support/feedburner/bin/answer.py?hl=en&answer=78464

        :param feedburner_bypass: If true, the redirect to feedburner is disabled.
        :rtype: Dict
        :returns:
            podcast
                A :class:`~mediacore.model.podcasts.Podcast` instance.
            episodes
                A list of :class:`~mediacore.model.media.Media` instances
                that belong to the ``podcast``.

        Renders: :data:`podcasts/feed.xml` XML

        """
        podcast = fetch_row(Podcast, slug=slug)

        if (
            podcast.feedburner_url
            and not "feedburner" in request.environ.get("HTTP_USER_AGENT", "").lower()
            and not kwargs.get("feedburner_bypass", False)
        ):
            redirect(podcast.feedburner_url.encode("utf-8"))

        response.content_type = content_type_for_response(["application/rss+xml", "application/xml", "text/xml"])

        episodes = podcast.media.published().order_by(Media.publish_on.desc())[:25]

        return dict(podcast=podcast, episodes=episodes)
Пример #15
0
    def jwplayer_rtmp_mrss(self, slug, **kwargs):
        """List the rtmp-playable files associated with this media item

        :param slug: The :attr:`~mediacore.models.media.Media.slug` to lookup
        :rtype dict:
        :returns:
            media
                The :class:`~mediacore.model.media.Media` instance for display.
            files
                A list of :class:`~mediacore.model.media.MediaFile` instances to display.

        """
        media = fetch_row(Media, slug=slug)
        rtmp_uris = pick_uris(media, scheme='rtmp')
        rtmp_uris = manager().sort_uris(rtmp_uris)
        can_play = JWPlayer.can_play(rtmp_uris)
        uris = [uri for uri, plays in izip(rtmp_uris, can_play) if plays]

        if not uris:
            raise HTTPNotFound()
        response.content_type = 'application/rss+xml'
        return dict(
            media = media,
            uris = uris,
        )
Пример #16
0
    def view(self, slug, page=1, show='latest', **kwargs):
        """View a podcast and the media that belongs to it.

        :param slug: A :attr:`~mediacore.model.podcasts.Podcast.slug`
        :param page: Page number, defaults to 1.
        :type page: int
        :rtype: dict
        :returns:
            podcast
                A :class:`~mediacore.model.podcasts.Podcast` instance.
            episodes
                A list of :class:`~mediacore.model.media.Media` instances
                that belong to the ``podcast``.
            podcasts
                A list of all the other podcasts

        """
        podcast = fetch_row(Podcast, slug=slug)
        episodes = podcast.media.published()

        episodes, show = helpers.filter_library_controls(episodes, show)

        episodes = viewable_media(episodes)
        
        if request.settings['rss_display'] == 'True':
            response.feed_links.append(
               (url_for(action='feed'), podcast.title)
            )

        return dict(
            podcast = podcast,
            episodes = episodes,
            result_count = episodes.count(),
            show = show,
        )
Пример #17
0
    def edit(self, id, **kwargs):
        """Display the :class:`~mediacore.forms.users.UserForm` for editing or adding.

        :param id: User ID
        :type id: ``int`` or ``"new"``
        :rtype: dict
        :returns:
            user
                The :class:`~mediacore.model.auth.User` instance we're editing.
            user_form
                The :class:`~mediacore.forms.users.UserForm` instance.
            user_action
                ``str`` form submit url
            user_values
                ``dict`` form values

        """
        user = fetch_row(User, id)

        if tmpl_context.action == "save" or id == "new":
            # Use the values from error_handler or GET for new users
            user_values = kwargs
            user_values["login_details.password"] = None
            user_values["login_details.confirm_password"] = None
        else:
            user_values = dict(
                display_name=user.display_name,
                email_address=user.email_address,
                login_details=dict(group=user.groups[0].group_id if user.groups else None, user_name=user.user_name),
            )

        return dict(user=user, user_form=user_form, user_action=url_for(action="save"), user_values=user_values)
Пример #18
0
    def save(self, id, delete, category='topics', **kwargs):
        """Save changes or create a topic or tag.

        See :class:`~mediacore.forms.categories.EditCategoryForm` for POST vars.

        :param id: Topic or tag ID
        :param category: ``topics`` or ``tags``
        :param delete: If true the category is deleted rather than saved.
        :type delete: bool
        :rtype: JSON dict
        :returns:
            success
                bool
            category
                ``topics`` or ``tags``

        """
        model = self.select_model(category)
        item = fetch_row(model, id)

        if delete:
            DBSession.delete(item)
            item = None
        else:
            item.name = kwargs['name']
            item.slug = get_available_slug(model, kwargs['slug'], item)

            DBSession.add(item)

        if request.is_xhr:
            return dict(success=True, category=item)
        else:
            redirect(action='index', category=category)
Пример #19
0
    def rate(self, slug, up=None, down=None, **kwargs):
        """Say 'I like this' for the given media.

        :param slug: The media :attr:`~mediacore.model.media.Media.slug`
        :rtype: unicode
        :returns:
            The new number of likes

        """
        media = fetch_row(Media, slug=slug)
        request.perm.assert_permission(u'view', media.resource)

        if up:
            if not request.settings['appearance_show_like']:
                abort(status_code=403)
            media.increment_likes()
        elif down:
            if not request.settings['appearance_show_dislike']:
                abort(status_code=403)
            media.increment_dislikes()

        if request.is_xhr:
            return u''
        else:
            redirect(action='view')
Пример #20
0
    def rate(self, slug, rating=1, **kwargs):
        """Rate up or down the given media.

        :param slug: The media :attr:`~mediacore.model.media.Media.slug`
        :param rating: ``1`` or ``0`` if the rating is up or down.
        :rtype: JSON dict
        :returns:
            succcess
                bool
            upRating
                Pluralized count of up raters, "# people" or "1 person"
            downRating
                Pluralized count of down raters, "# people" or "1 person"

        """
        media = fetch_row(Media, slug=slug)

        if rating > 0:
            media.rating.add_vote(1)
        else:
            media.rating.add_vote(0)
        DBSession.add(media)

        if request.is_xhr:
            return dict(
                success = True,
                upRating = helpers.text.plural(media.rating.sum, 'person', 'people'),
                downRating = None,
            )
        else:
            redirect(action='view')
Пример #21
0
    def comment(self, slug, name="", email=None, body="", **kwargs):
        """Post a comment from :class:`~mediacore.forms.comments.PostCommentForm`.

        :param slug: The media :attr:`~mediacore.model.media.Media.slug`
        :returns: Redirect to :meth:`view` page for media.

        """

        def result(success, message=None, comment=None):
            if request.is_xhr:
                result = dict(success=success, message=message)
                if comment:
                    result["comment"] = render("comments/_list.html", {"comment_to_render": comment}, method="xhtml")
                return result
            elif success:
                return redirect(action="view")
            else:
                return self.view(slug, name=name, email=email, body=body, **kwargs)

        akismet_key = request.settings["akismet_key"]
        if akismet_key:
            akismet = Akismet(agent=USER_AGENT)
            akismet.key = akismet_key
            akismet.blog_url = request.settings["akismet_url"] or url_for("/", qualified=True)
            akismet.verify_key()
            data = {
                "comment_author": name.encode("utf-8"),
                "user_ip": request.environ.get("REMOTE_ADDR"),
                "user_agent": request.environ.get("HTTP_USER_AGENT", ""),
                "referrer": request.environ.get("HTTP_REFERER", "unknown"),
                "HTTP_ACCEPT": request.environ.get("HTTP_ACCEPT"),
            }

            if akismet.comment_check(body.encode("utf-8"), data):
                return result(False, _(u"Your comment has been rejected."))

        media = fetch_row(Media, slug=slug)
        request.perm.assert_permission(u"view", media.resource)

        c = Comment()

        name = filter_vulgarity(name)
        c.author = AuthorWithIP(name, email, request.environ["REMOTE_ADDR"])
        c.subject = "Re: %s" % media.title
        c.body = filter_vulgarity(body)

        require_review = request.settings["req_comment_approval"]
        if not require_review:
            c.reviewed = True
            c.publishable = True

        media.comments.append(c)
        DBSession.flush()
        send_comment_notification(media, c)

        if require_review:
            message = _("Thank you for your comment! We will post it just as " "soon as a moderator approves it.")
            return result(True, message=message)
        else:
            return result(True, comment=c)
Пример #22
0
    def comment(self, slug, **values):
        """Post a comment from :class:`~mediacore.forms.media.PostCommentForm`.

        :param slug: The media :attr:`~mediacore.model.media.Media.slug`
        :returns: Redirect to :meth:`view` page for media.

        """
        if tmpl_context.form_errors:
            if request.is_xhr:
                return dict(
                    success = False,
                    errors = tmpl_context.form_errors
                )
            else:
                redirect(action='view')

        media = fetch_row(Media, slug=slug)
        c = Comment()
        c.status = 'unreviewed'
        c.author = AuthorWithIP(values['name'], None, request.environ['REMOTE_ADDR'])
        c.subject = 'Re: %s' % media.title
        c.body = helpers.clean_xhtml(values['body'])

        media.comments.append(c)
        DBSession.add(media)
        email.send_comment_notification(media, c)

        if request.is_xhr:
            return dict(success = True)
        else:
            redirect(action='view')
Пример #23
0
    def edit(self, id, engine_type=None, **kwargs):
        """Display the :class:`~mediacore.lib.storage.StorageEngine` for editing or adding.

        :param id: Storage ID
        :type id: ``int`` or ``"new"``
        :rtype: dict
        :returns:

        """
        if id != "new":
            engine = fetch_row(StorageEngine, id)
        else:
            types = dict((cls.engine_type, cls) for cls in StorageEngine)
            engine_cls = types.get(engine_type, None)
            if not engine_cls:
                redirect(controller="/admin/storage", action="index")
            engine = engine_cls()

            if not engine.settings_form:
                # XXX: If this newly created storage engine has no settings,
                #      just save it. This isn't RESTful (as edit is a GET
                #      action), but it simplifies the creation process.
                DBSession.add(engine)
                redirect(controller="/admin/storage", action="index")

        return {
            "engine": engine,
            "form": engine.settings_form,
            "form_action": url_for(action="save", engine_type=engine_type),
            "form_values": kwargs,
        }
Пример #24
0
 def panda_retry(self, file_id, encoding_id, **kwargs):
     media_file = fetch_row(MediaFile, file_id)
     storage = DBSession.query(PandaStorage).first()
     storage.panda_helper().retry_transcode(media_file, encoding_id)
     return dict(
         success = True,
     )
Пример #25
0
    def serve(self, id, download=False, **kwargs):
        """Serve a :class:`~mediacore.model.media.MediaFile` binary.

        :param id: File ID
        :type id: ``int``
        :param bool download: If true, serve with an Content-Disposition that
            makes the file download to the users computer instead of playing
            in the browser.
        :raises webob.exc.HTTPNotFound: If no file exists with this ID.
        :raises webob.exc.HTTPNotAcceptable: If an Accept header field
            is present, and if the mimetype of the requested file doesn't
            match, then a 406 (not acceptable) response is returned.

        """
        file = fetch_row(MediaFile, id=id)

        file_path = helpers.file_path(file).encode('utf-8')
        file_type = file.mimetype.encode('utf-8')
        file_name = file.display_name.encode('utf-8')

        if not os.path.exists(file_path):
            log.warn('No such file or directory: %r', file_path)
            raise HTTPNotFound()

        # Ensure the request accepts files with this container
        accept = request.environ.get('HTTP_ACCEPT', '*/*')
        if not mimeparse.best_match([file_type], accept):
            raise HTTPNotAcceptable() # 406

        method = config.get('file_serve_method', None)
        headers = []

        # Serving files with this header breaks playback on iPhone
        if download:
            headers.append(('Content-Disposition',
                            'attachment; filename="%s"' % file_name))

        if method == 'apache_xsendfile':
            # Requires mod_xsendfile for Apache 2.x
            # XXX: Don't send Content-Length or Etag headers,
            #      Apache handles them for you.
            response.headers['X-Sendfile'] = file_path
            response.body = ''
            response.headers.update(headers)
        elif method == 'nginx_redirect':
            for header in ['pragma', 'cache-control', "content-type"]:
                if header in response.headers:
                    del response.headers[header]
            response.headers['X-Accel-Redirect'] = file_path
            response.headers.update(headers)
        else:
            app = FileApp(file_path, headers, content_type=file_type)
            return forward(app)

        response.headers['Content-Type'] = file_type
        for header, value in headers:
            response.headers[header] = value

        return None
Пример #26
0
    def save(self,
             id,
             email_address,
             display_name,
             login_details,
             delete=None,
             **kwargs):
        """Save changes or create a new :class:`~mediacore.model.auth.User` instance.

        :param id: User ID. If ``"new"`` a new user is created.
        :type id: ``int`` or ``"new"``
        :returns: Redirect back to :meth:`index` after successful save.

        """
        user = fetch_row(User, id)

        if delete:
            DBSession.delete(user)
            redirect(action='index', id=None)

        user.display_name = display_name
        user.email_address = email_address
        user.user_name = login_details['user_name']

        password = login_details['password']
        if password is not None and password != '':
            user.password = password

        if login_details['group']:
            group = fetch_row(Group, login_details['group'])
            user.groups = [group]
        else:
            user.groups = []

        DBSession.add(user)

        # Check if we're changing the logged in user's own password
        logged_in_user = request.environ['repoze.who.identity']['user']
        if user.user_id == logged_in_user.user_id \
        and password is not None and password != '':
            DBSession.commit()
            # repoze.who sees the Unauthorized response and clears the cookie,
            # forcing a fresh login with the new password
            raise webob.exc.HTTPUnauthorized().exception

        redirect(action='index', id=None)
Пример #27
0
 def embed_player(self, slug, w=None, h=None, **kwargs):
     media = fetch_row(Media, slug=slug)
     request.perm.assert_permission(u'view', media.resource)
     return dict(
         media = media,
         width = w and int(w) or None,
         height = h and int(h) or None,
     )
Пример #28
0
    def view(self, slug, podcast_slug=None, **kwargs):
        """Display the media player, info and comments.

        :param slug: The :attr:`~mediacore.models.media.Media.slug` to lookup
        :param podcast_slug: The :attr:`~mediacore.models.podcasts.Podcast.slug`
            for podcast this media belongs to. Although not necessary for
            looking up the media, it tells us that the podcast slug was
            specified in the URL and therefore we reached this action by the
            preferred route.
        :rtype dict:
        :returns:
            media
                The :class:`~mediacore.model.media.Media` instance for display.
            comment_form
                The :class:`~mediacore.forms.comments.PostCommentForm` instance.
            comment_form_action
                ``str`` comment form action
            comment_form_values
                ``dict`` form values
            next_episode
                The next episode in the podcast series, if this media belongs to
                a podcast, another :class:`~mediacore.model.media.Media`
                instance.

        """
        media = fetch_row(Media, slug=slug)

        if media.podcast_id is not None:
            # Always view podcast media from a URL that shows the context of the podcast
            if url_for() != url_for(podcast_slug=media.podcast.slug):
                redirect(podcast_slug=media.podcast.slug)

        if media.fulltext:
            search_terms = '%s %s' % (media.title, media.fulltext.tags)
            related = Media.query.published()\
                .options(orm.undefer('comment_count_published'))\
                .filter(Media.id != media.id)\
                .search(search_terms, bool=False)
        else:
            related = []

        media.increment_views()

        # Which style of 'likes' links has the admin selected?
        # TODO: Add settings to control these options.
        mediacore_likes = True
        facebook_likes = False

        return dict(
            media = media,
            related_media = related[:6],
            comments = media.comments.published().all(),
            comment_form = post_comment_form,
            comment_form_action = url_for(action='comment', anchor=post_comment_form.id),
            comment_form_values = kwargs,
            mediacore_likes = mediacore_likes,
            facebook_likes = facebook_likes,
        )
    def prepareForUpload(self, environ, media_id, content_type, filename, filesize, meta=None, **kwargs):
        STORAGE_ENGINE = getStorageEngine()
        log.info("{self}.prepareForUpload({media_id},{content_type},{filename},{filesize})".format(**vars()))

        if not meta:
            meta = {}
        else:
            try:
                meta = json.loads(meta)
            except Exception as e:
                return {"success": False, "message": "Invalid JSON object given for `meta`"}

        media = fetch_row(Media, media_id)
        mediaFile = MediaFile()
        mediaFile.storage = STORAGE_ENGINE
        mediaFile.media = media
        mediaFile.media_id = media_id
        mediaFile.type = content_type
        mediaFile.meta = meta
        media.type = content_type
        mediaFile.display_name = filename
        mediaFile.size = filesize
        media.files.append(mediaFile)

        DBSession.add(media)
        DBSession.add(mediaFile)
        DBSession.flush()

        # This is to ensure that we don't allow any uploads that haven't been prepared for with prepareForUpload
        token = "".join(random.choice(string.ascii_uppercase + string.digits) for x in range(13))
        upload_tokens[str(mediaFile.id)] = token

        return {
            "success": True,
            "id": mediaFile.id,
            "upload_url": "http://{host}{path}".format(
                host=environ["HTTP_HOST"],
                path=url_for(
                    controller="upload_api/api/uploader", action="uploadFile", media_id=media_id, file_id=mediaFile.id
                ),
            ),
            "upload_headers": {
                "Content-Type": "application/octet-stream",
                "Cache-Control": "none",
                "X-File-Name": filename,
                "X-Upload-Token": token,
            },
            "postprocess_url": "http://{host}{path}".format(
                host=environ["HTTP_HOST"],
                path=url_for(
                    controller="upload_api/api/uploader",
                    action="postprocessFile",
                    media_id=media_id,
                    file_id=mediaFile.id,
                ),
            ),
        }
Пример #30
0
def fetch_setting(key):
    """Return the value for the setting key.

    Raises a SettingNotFound exception if the key does not exist.
    """
    try:
        return fetch_row(Setting, key=unicode(key)).value
    except HTTPNotFound:
        raise SettingNotFound, 'Key not found: %s' % key
Пример #31
0
    def view(self, slug, podcast_slug=None, **kwargs):
        """Display the media player, info and comments.

        :param slug: The :attr:`~mediacore.models.media.Media.slug` to lookup
        :param podcast_slug: The :attr:`~mediacore.models.podcasts.Podcast.slug`
            for podcast this media belongs to. Although not necessary for
            looking up the media, it tells us that the podcast slug was
            specified in the URL and therefore we reached this action by the
            preferred route.
        :rtype dict:
        :returns:
            media
                The :class:`~mediacore.model.media.Media` instance for display.
            related_media
                A list of :class:`~mediacore.model.media.Media` instances that
                rank as topically related to the given media item.
            comments
                A list of :class:`~mediacore.model.comments.Comment` instances
                associated with the selected media item.
            comment_form_action
                ``str`` comment form action
            comment_form_values
                ``dict`` form values
            next_episode
                The next episode in the podcast series, if this media belongs to
                a podcast, another :class:`~mediacore.model.media.Media`
                instance.

        """
        media = fetch_row(Media, slug=slug)
        request.perm.assert_permission(u'view', media.resource)

        if media.podcast_id is not None:
            # Always view podcast media from a URL that shows the context of the podcast
            if url_for() != url_for(podcast_slug=media.podcast.slug):
                redirect(podcast_slug=media.podcast.slug)

        try:
            media.increment_views()
            DBSession.commit()
        except OperationalError:
            DBSession.rollback()

        if request.settings['comments_engine'] == 'facebook':
            response.facebook = Facebook(request.settings['facebook_appid'])

        related_media = viewable_media(Media.query.related(media))[:6]
        # TODO: finish implementation of different 'likes' buttons
        #       e.g. the default one, plus a setting to use facebook.
        return dict(
            media = media,
            related_media = related_media,
            comments = media.comments.published().all(),
            comment_form_action = url_for(action='comment'),
            comment_form_values = kwargs,
        )
Пример #32
0
    def index(self, page=1, show='latest', q=None, tag=None, **kwargs):
        """List media with pagination.

        The media paginator may be accessed in the template with
        :attr:`c.paginators.media`, see :class:`webhelpers.paginate.Page`.

        :param page: Page number, defaults to 1.
        :type page: int
        :param show: 'latest', 'popular' or 'featured'
        :type show: unicode or None
        :param q: A search query to filter by
        :type q: unicode or None
        :param tag: A tag slug to filter for
        :type tag: unicode or None
        :rtype: dict
        :returns:
            media
                The list of :class:`~mediacore.model.media.Media` instances
                for this page.
            result_count
                The total number of media items for this query
            search_query
                The query the user searched for, if any

        """
        media = Media.query.published()

        media, show = helpers.filter_library_controls(media, show)

        if q:
            media = media.search(q, bool=True)

        if tag:
            tag = fetch_row(Tag, slug=tag)
            media = media.filter(Media.tags.contains(tag))

        if (request.settings['rss_display'] == 'True') and (not (q or tag)):
            if show == 'latest':
                response.feed_links.extend([
                    (url_for(controller='/sitemaps',
                             action='latest'), _(u'Latest RSS')),
                ])
            elif show == 'featured':
                response.feed_links.extend([
                    (url_for(controller='/sitemaps',
                             action='featured'), _(u'Featured RSS')),
                ])

        media = viewable_media(media)
        return dict(
            media=media,
            result_count=media.count(),
            search_query=q,
            show=show,
            tag=tag,
        )
Пример #33
0
 def fetch_engine(self, id, engine_type=None):
     if id != 'new':
         engine = fetch_row(StorageEngine, id)
     else:
         types = dict((cls.engine_type, cls) for cls in StorageEngine)
         engine_cls = types.get(engine_type, None)
         if not engine_cls:
             redirect(controller='/admin/storage', action='index')
         engine = engine_cls()
     return engine
Пример #34
0
    def disable(self, id, **kwargs):
        """Disable a StorageEngine.

        :param id: engine ID.
        :type id: ``int``
        :returns: Redirect back to :meth:`index` after success.
        """
        engine = fetch_row(StorageEngine, id)
        engine.enabled = False
        redirect(action='index', id=None)
    def uploadFile(self, environ, media_id, file_id, **kwargs):
        log.info("uploadFile({media_id},{file_id})".format(**vars()))

        media = fetch_row(Media, media_id)
        mediaFile = fetch_row(MediaFile, file_id)

        # Requests not carrying a valid X-Upload-Token must be rejected immediately
        if 'HTTP_X_UPLOAD_TOKEN' not in environ or str(
                file_id) not in upload_tokens.keys():
            raise webob.exc.HTTPForbidden().exception
        elif not environ['HTTP_X_UPLOAD_TOKEN'] == upload_tokens[str(file_id)]:
            raise webob.exc.HTTPForbidden().exception

        STORAGE_ENGINE = getStorageEngine()

        class FileEntry(object):
            def __init__(self, file, name=None):
                self.file = file
                self.filename = name if name else file.name

        unique_id = STORAGE_ENGINE.store(media_file=mediaFile,
                                         file=FileEntry(
                                             environ['wsgi.input'],
                                             mediaFile.display_name))

        try:
            STORAGE_ENGINE.transcode(mediaFile)
        except Exception:
            log.debug('Engine %r unsuitable for transcoding %r',
                      STORAGE_ENGINE, mediaFile)

        mediaFile.container = os.path.splitext(mediaFile.display_name)[1][1:]
        if unique_id:
            mediaFile.unique_id = unique_id

        DBSession.add(mediaFile)
        DBSession.flush()

        del upload_tokens[str(file_id)]

        # Ideally we'd determine information about the uploaded media file and return it here.
        return {}
Пример #36
0
    def disable(self, id, **kwargs):
        """Disable a PlayerPref.

        :param id: Player ID.
        :type id: ``int``
        :returns: Redirect back to :meth:`index` after success.
        """
        player = fetch_row(PlayerPrefs, id)
        player.enabled = False
        update_enabled_players()
        redirect(action='index', id=None)
Пример #37
0
    def update_status(self, id, status=None, publish_on=None, publish_until=None, **values):
        """Update the publish status for the given media.

        :param id: Media ID
        :type id: ``int``
        :param update_status: The text of the submit button which indicates
            that the :attr:`~mediacore.model.media.Media.status` should change.
        :type update_status: ``unicode`` or ``None``
        :param publish_on: A date to set to
            :attr:`~mediacore.model.media.Media.publish_on`
        :type publish_on: :class:`datetime.datetime` or ``None``
        :param publish_until: A date to set to
            :attr:`~mediacore.model.media.Media.publish_until`
        :type publish_until: :class:`datetime.datetime` or ``None``
        :rtype: JSON dict
        :returns:
            success
                bool
            message
                Error message, if unsuccessful
            status_form
                Rendered XHTML for the status form, updated to reflect the
                changes made.

        """
        media = fetch_row(Media, id)
        new_slug = None

        # Make the requested change assuming it will be allowed
        if status == 'unreviewed':
            media.reviewed = True
        elif status == 'draft':
            self._publish_media(media, publish_on)
        elif publish_on:
            media.publish_on = publish_on
            media.update_popularity()
        elif publish_until:
            media.publish_until = publish_until

        # Verify the change is valid by re-determining the status
        media.update_status()
        DBSession.flush()

        if request.is_xhr:
            # Return the rendered widget for injection
            status_form_xhtml = unicode(update_status_form.display(
                action=url_for(action='update_status'), media=media))
            return dict(
                success = True,
                status_form = status_form_xhtml,
                slug = new_slug,
            )
        else:
            redirect(action='edit')
Пример #38
0
    def _assert_succesful_media_upload(self):
        media = fetch_row(Media, slug=u'testing-mp3-async-upload')
        assert_equals('John Doe', media.author.name)
        assert_equals('*****@*****.**', media.author.email)
        assert_equals('testing mp3 async upload', media.title)
        assert_equals('<p>a great song</p>', media.description)

        assert_length(1, media.files)
        media_file = media.files[0]
        assert_equals('mp3', media_file.container)
        assert_equals('awesome-song.mp3', media_file.display_name)
        return media
Пример #39
0
    def save(self,
             id,
             slug,
             title,
             subtitle,
             author_name,
             author_email,
             description,
             details,
             feed,
             delete=None,
             **kwargs):
        """Save changes or create a new :class:`~mediacore.model.podcasts.Podcast` instance.

        Form handler the :meth:`edit` action and the
        :class:`~mediacore.forms.admin.podcasts.PodcastForm`.

        Redirects back to :meth:`edit` after successful editing
        and :meth:`index` after successful deletion.

        """
        podcast = fetch_row(Podcast, id)

        if delete:
            file_paths = thumb_paths(podcast).values()
            DBSession.delete(podcast)
            DBSession.commit()
            helpers.delete_files(file_paths, Podcast._thumb_dir)
            redirect(action='index', id=None)

        if not slug:
            slug = title
        if slug != podcast.slug:
            podcast.slug = get_available_slug(Podcast, slug, podcast)
        podcast.title = title
        podcast.subtitle = subtitle
        podcast.author = Author(author_name, author_email)
        podcast.description = description
        podcast.copyright = details['copyright']
        podcast.category = details['category']
        podcast.itunes_url = feed['itunes_url']
        podcast.feedburner_url = feed['feedburner_url']
        podcast.explicit = {
            'yes': True,
            'clean': False
        }.get(details['explicit'], None)

        if id == 'new':
            DBSession.add(podcast)
            DBSession.flush()
            create_default_thumbs_for(podcast)

        redirect(action='edit', id=podcast.id)
Пример #40
0
    def save(self, id, slug, title, author_name, author_email,
             description, notes, podcast, tags, categories,
             delete=None, **kwargs):
        """Save changes or create a new :class:`~mediacore.model.media.Media` instance.

        Form handler the :meth:`edit` action and the
        :class:`~mediacore.forms.admin.media.MediaForm`.

        Redirects back to :meth:`edit` after successful editing
        and :meth:`index` after successful deletion.

        """
        media = fetch_row(Media, id)

        if delete:
            self._delete_media(media)
            DBSession.commit()
            redirect(action='index', id=None)

        if not slug:
            slug = title
        elif slug.startswith('_stub_'):
            slug = slug[len('_stub_'):]
        if slug != media.slug:
            media.slug = get_available_slug(Media, slug, media)
        media.title = title
        media.author = Author(author_name, author_email)
        media.description = description
        media.notes = notes
        media.podcast_id = podcast
        media.set_tags(tags)
        media.set_categories(categories)

        media.update_status()
        DBSession.add(media)
        DBSession.flush()

        if id == 'new' and not has_thumbs(media):
            create_default_thumbs_for(media)

        if request.is_xhr:
            status_form_xhtml = unicode(update_status_form.display(
                action=url_for(action='update_status', id=media.id),
                media=media))

            return dict(
                media_id = media.id,
                values = {'slug': slug},
                link = url_for(action='edit', id=media.id),
                status_form = status_form_xhtml,
            )
        else:
            redirect(action='edit', id=media.id)
Пример #41
0
    def delete(self, id, **kwargs):
        """Delete a StorageEngine.

        :param id: Storage ID.
        :type id: ``int``
        :returns: Redirect back to :meth:`index` after successful delete.
        """
        engine = fetch_row(StorageEngine, id)
        files = engine.files
        for f in files:
            engine.delete(f.unique_id)
        DBSession.delete(engine)
        redirect(action='index', id=None)
Пример #42
0
    def delete(self, id, **kwargs):
        """Delete a user.

        :param id: User ID.
        :type id: ``int``
        :returns: Redirect back to :meth:`index` after successful delete.
        """
        user = fetch_row(User, id)
        DBSession.delete(user)

        if request.is_xhr:
            return dict(success=True)
        redirect(action='index', id=None)
Пример #43
0
    def delete(self, id, **kwargs):
        """Delete a group.

        :param id: Group ID.
        :type id: ``int``
        :returns: Redirect back to :meth:`index` after successful delete.
        """
        group = fetch_row(Group, id)
        DBSession.delete(group)

        if request.is_xhr:
            return dict(success=True)
        redirect(action='index', id=None)
Пример #44
0
    def test_submit_async(self):
        fields, files = self._valid_values('testing mp3 async upload')
        submit_url = url(controller='upload', action='submit_async')
        success_url = url(controller='upload', action='success')

        response = self.app.post(submit_url, params=fields, upload_files=files)
        assert response.status_int == 200
        assert response.headers['Content-Type'] == 'application/json'
        assert response.body == '{"redirect": "/upload/success", "success": true}'

        media = fetch_row(Media, slug=u'testing-mp3-async-upload')
        assert len(media.files) == 1
        assert media.files[0].container == 'mp3'
        assert media.description == "<p>actually just testing an mp3 upload.</p>"
Пример #45
0
    def view(self, slug, podcast_slug=None, **kwargs):
        """Display the media player, info and comments.

        :param slug: The :attr:`~mediacore.models.media.Media.slug` to lookup
        :param podcast_slug: The :attr:`~mediacore.models.podcasts.Podcast.slug`
            for podcast this media belongs to. Although not necessary for
            looking up the media, it tells us that the podcast slug was
            specified in the URL and therefore we reached this action by the
            preferred route.
        :rtype dict:
        :returns:
            media
                The :class:`~mediacore.model.media.Media` instance for display.
            comment_form
                The :class:`~mediacore.forms.comments.PostCommentForm` instance.
            comment_form_action
                ``str`` comment form action
            comment_form_values
                ``dict`` form values
            next_episode
                The next episode in the podcast series, if this media belongs to
                a podcast, another :class:`~mediacore.model.media.Media`
                instance.

        """
        media = fetch_row(Media, slug=slug)

        if media.podcast_id is not None:
            # Always view podcast media from a URL that shows the context of the podcast
            if url_for() != url_for(podcast_slug=media.podcast.slug):
                redirect(podcast_slug=media.podcast.slug)

        media.increment_views()

        # Which style of 'likes' links has the admin selected?
        # TODO: Add settings to control these options.
        mediacore_likes = True
        facebook_likes = False

        return dict(
            media=media,
            related_media=Media.query.related(media)[:6],
            comments=media.comments.published().all(),
            comment_form_action=url_for(action='comment'),
            comment_form_values=kwargs,
            mediacore_likes=mediacore_likes,
            facebook_likes=facebook_likes,
        )
Пример #46
0
    def save(self, id, **kwargs):
        player = fetch_row(PlayerPrefs, id)
        form = player.settings_form

        if id == 'new':
            DBSession.add(player)

        @validate(form, error_handler=self.edit)
        def save(id, **kwargs):
            # Allow the form to modify the player directly
            # since each can have radically different fields.
            save_func = getattr(form, 'save_data')
            save_func(player, **tmpl_context.form_values)
            redirect(controller='/admin/players', action='index')

        return save(id, **kwargs)
Пример #47
0
    def save_thumb(self, id, thumb, **kwargs):
        """Save a thumbnail uploaded with :class:`~mediacore.forms.admin.ThumbForm`.

        :param id: Media ID. If ``"new"`` a new Media stub is created.
        :type id: ``int`` or ``"new"``
        :param file: The uploaded file
        :type file: :class:`cgi.FieldStorage` or ``None``
        :rtype: JSON dict
        :returns:
            success
                bool
            message
                Error message, if unsuccessful
            id
                The :attr:`~mediacore.model.media.Media.id` which is
                important if a new media has just been created.

        """
        if id == 'new':
            media = Media()
            user = req.perm.user
            media.author = Author(user.display_name, user.email_address)
            media.title = os.path.basename(thumb.filename)
            media.slug = get_available_slug(Media, '_stub_' + media.title)
            DBSession.add(media)
            DBSession.flush()
        else:
            media = fetch_row(Media, id)

        try:
            # Create JPEG thumbs
            create_thumbs_for(media, thumb.file, thumb.filename)
            success = True
            message = None
        except IOError, e:
            success = False
            if id == 'new':
                DBSession.delete(media)
            if e.errno == 13:
                message = _('Permission denied, cannot write file')
            elif e.message == 'cannot identify image file':
                message = _('Unsupported image type: %s') \
                    % os.path.splitext(thumb.filename)[1].lstrip('.')
            elif e.message == 'cannot read interlaced PNG files':
                message = _('Interlaced PNGs are not supported.')
            else:
                raise
Пример #48
0
    def save(self,
             id,
             email_address,
             display_name,
             login_details,
             delete=None,
             **kwargs):
        """Save changes or create a new :class:`~mediacore.model.auth.User` instance.

        :param id: User ID. If ``"new"`` a new user is created.
        :type id: ``int`` or ``"new"``
        :returns: Redirect back to :meth:`index` after successful save.

        """
        user = fetch_row(User, id)

        if delete:
            DBSession.delete(user)
            redirect(action='index', id=None)

        user.display_name = display_name
        user.email_address = email_address
        user.user_name = login_details['user_name']

        password = login_details['password']
        if password is not None and password != '':
            user.password = password

        if login_details['groups']:
            query = DBSession.query(Group).filter(
                Group.group_id.in_(login_details['groups']))
            user.groups = list(query.all())
        else:
            user.groups = []

        DBSession.add(user)

        # Check if we're changing the logged in user's own password
        if user.id == request.perm.user.id \
        and password is not None and password != '':
            DBSession.commit()
            # repoze.who sees the Unauthorized response and clears the cookie,
            # forcing a fresh login with the new password
            raise webob.exc.HTTPUnauthorized().exception

        redirect(action='index', id=None)
Пример #49
0
    def edit(self, id, name=None, **kwargs):
        """Display the :class:`~mediacore.model.players.PlayerPrefs` for editing or adding.

        :param id: PlayerPrefs ID
        :type id: ``int`` or ``"new"``
        :rtype: dict
        :returns:

        """
        playerp = fetch_row(PlayerPrefs, id)

        return {
            'player': playerp,
            'form': playerp.settings_form,
            'form_action': url_for(action='save'),
            'form_values': kwargs,
        }
Пример #50
0
    def delete(self, id, **kwargs):
        """Delete a PlayerPref.

        After deleting the PlayerPref, cleans up the players table,
        ensuring that each Player class is represented--if the deleted
        PlayerPref is the last example of that Player class, creates a new
        disabled PlayerPref for that Player class with the default settings.

        :param id: Player ID.
        :type id: ``int``
        :returns: Redirect back to :meth:`index` after successful delete.
        """
        player = fetch_row(PlayerPrefs, id)
        DBSession.delete(player)
        DBSession.flush()
        cleanup_players_table()
        redirect(action='index', id=None)
Пример #51
0
    def view(self, slug, podcast_slug=None, **kwargs):
        """Display the media player, info and comments.

        :param slug: The :attr:`~mediacore.models.media.Media.slug` to lookup
        :param podcast_slug: The :attr:`~mediacore.models.podcasts.Podcast.slug`
            for podcast this media belongs to. Although not necessary for
            looking up the media, it tells us that the podcast slug was
            specified in the URL and therefore we reached this action by the
            preferred route.
        :rtype dict:
        :returns:
            media
                The :class:`~mediacore.model.media.Media` instance for display.
            related_media
                A list of :class:`~mediacore.model.media.Media` instances that
                rank as topically related to the given media item.
            comments
                A list of :class:`~mediacore.model.comments.Comment` instances
                associated with the selected media item.
            comment_form_action
                ``str`` comment form action
            comment_form_values
                ``dict`` form values
            next_episode
                The next episode in the podcast series, if this media belongs to
                a podcast, another :class:`~mediacore.model.media.Media`
                instance.

        """
        media = fetch_row(Media, slug=slug)

        if media.podcast_id is not None:
            # Always view podcast media from a URL that shows the context of the podcast
            if url_for() != url_for(podcast_slug=media.podcast.slug):
                redirect(podcast_slug=media.podcast.slug)

        media.increment_views()

        # TODO: finish implementation of different 'likes' buttons
        #       e.g. the default one, plus a setting to use facebook.
        return dict(media=media,
                    related_media=Media.query.related(media)[:6],
                    comments=media.comments.published().all(),
                    comment_form_action=url_for(action='comment'),
                    comment_form_values=kwargs,
                    can_comment=self.can_comment())
    def test_add_new_media(self):
        new_url = url(controller='admin/media', action='edit', id='new')
        save_url = url(controller='admin/media', action='save', id='new')

        title = 'Add New Media Test'
        slug = u'add-new-media-test'  # this should be unique
        name = 'Frederick Awesomeson'
        email = '*****@*****.**'
        description = 'This media item was created to test the "admin/media/edit/new" method'
        htmlized_description = '<p>This media item was created to test the &quot;admin/media/edit/new&quot; method</p>'

        self._login()
        new_response = self.app.get(new_url, status=200)
        form = new_response.forms['media-form']
        form['title'] = title
        form['author_name'] = name
        form['author_email'] = email
        form['description'] = description
        # form['categories']
        # form['tags']
        form['notes'] = ''
        assert form.action == save_url

        save_response = form.submit()

        # Ensure that the correct redirect was issued
        assert save_response.status_int == 302
        media = fetch_row(Media, slug=slug)
        edit_url = url(controller='admin/media', action='edit', id=media.id)
        assert save_response.location == 'http://localhost%s' % edit_url

        # Ensure that the media object was correctly created
        assert media.title == title
        assert media.author.name == name
        assert media.author.email == email
        assert media.description == htmlized_description

        # Ensure that the edit form is correctly filled out
        edit_response = save_response.follow()
        form = edit_response.forms['media-form']
        assert form['title'].value == title
        assert form['author_name'].value == name
        assert form['author_email'].value == email
        assert form['slug'].value == slug
        assert form['description'].value == htmlized_description
        assert form['notes'].value == ''
Пример #53
0
    def rate(self, slug, up=None, down=None, **kwargs):
        """Say 'I like this' for the given media.

        :param slug: The media :attr:`~mediacore.model.media.Media.slug`
        :rtype: unicode
        :returns:
            The new number of likes

        """
        # we have to check if current user is anonymous or authenticated
        userid = check_user_authentication(request)
        if not userid:
            log.warn('Anonymous user cannot rate media')
            raise HTTPUnauthorized().exception

        media = fetch_row(Media, slug=slug)

        # check if current user has already voted this media object
        votes = Vote.query.get_votes(media_id=media.id, user_name=userid)
        if votes.count():
            # if true redirect to 'view'
            log.warn('User %s already voted this media')
            redirect(action='view')

        # create new vote object mapping current media and user
        vote = Vote()
        vote.media_id = media.id
        vote.user_name = userid

        # Give the Vote object an ID.
        DBSession.add(vote)
        DBSession.flush()

        if up:
            vote.increment_likes()
            media.increment_likes()
        elif down:
            vote.increment_dislikes()
            media.increment_dislikes()

        if request.is_xhr:
            return u''
        else:
            redirect(action='view')
Пример #54
0
    def edit(self, id, **kwargs):
        """Display the :class:`~mediacore.forms.admin.users.UserForm` for editing or adding.

        :param id: User ID
        :type id: ``int`` or ``"new"``
        :rtype: dict
        :returns:
            user
                The :class:`~mediacore.model.auth.User` instance we're editing.
            user_form
                The :class:`~mediacore.forms.admin.users.UserForm` instance.
            user_action
                ``str`` form submit url
            user_values
                ``dict`` form values

        """
        user = fetch_row(User, id)

        if tmpl_context.action == 'save' or id == 'new':
            # Use the values from error_handler or GET for new users
            user_values = kwargs
            user_values['login_details.password'] = None
            user_values['login_details.confirm_password'] = None
        else:
            group_ids = None
            if user.groups:
                group_ids = map(lambda group: group.group_id, user.groups)
            user_values = dict(
                display_name = user.display_name,
                email_address = user.email_address,
                login_details = dict(
                    groups = group_ids,
                    user_name = user.user_name,
                ),
            )

        return dict(
            user = user,
            user_form = user_form,
            user_action = url_for(action='save'),
            user_values = user_values,
        )
Пример #55
0
    def edit(self, id, **kwargs):
        """Edit a single tag.

        :param id: Tag ID
        :rtype: Dict
        :returns:
            tags
                The list of :class:`~mediacore.model.tags.Tag`
                instances for this page.
            tag_form
                The :class:`~mediacore.forms.admin.settings.tags.TagForm` instance.

        """
        tag = fetch_row(Tag, id)

        return dict(
            tag = tag,
            tag_form = tag_form,
        )
Пример #56
0
    def save_thumb(self, id, thumb, **values):
        """Save a thumbnail uploaded with :class:`~mediacore.forms.admin.ThumbForm`.

        :param id: Media ID. If ``"new"`` a new Podcast stub is created.
        :type id: ``int`` or ``"new"``
        :param file: The uploaded file
        :type file: :class:`cgi.FieldStorage` or ``None``
        :rtype: JSON dict
        :returns:
            success
                bool
            message
                Error message, if unsuccessful
            id
                The :attr:`~mediacore.model.podcasts.Podcast.id` which is
                important if a new podcast has just been created.

        """
        if id == 'new':
            return dict(
                success=False,
                message=
                u'You must first save the podcast before you can upload a thumbnail',
            )

        podcast = fetch_row(Podcast, id)

        try:
            # Create JPEG thumbs
            create_thumbs_for(podcast, thumb.file, thumb.filename)
            success = True
            message = None
        except IOError, e:
            success = False
            if e.errno == 13:
                message = _('Permission denied, cannot write file')
            elif e.message == 'cannot identify image file':
                message = _('Unsupport image type: %s') \
                    % os.path.splitext(thumb.filename)[1].lstrip('.')
            elif e.message == 'cannot read interlaced PNG files':
                message = _('Interlaced PNGs are not supported.')
            else:
                raise
Пример #57
0
    def index(self, page=1, show='latest', q=None, tag=None, **kwargs):
        """List media with pagination.

        The media paginator may be accessed in the template with
        :attr:`c.paginators.media`, see :class:`webhelpers.paginate.Page`.

        :param page: Page number, defaults to 1.
        :type page: int
        :param show: 'latest', 'popular' or 'featured'
        :type show: unicode or None
        :param q: A search query to filter by
        :type q: unicode or None
        :param tag: A tag slug to filter for
        :type tag: unicode or None
        :rtype: dict
        :returns:
            media
                The list of :class:`~mediacore.model.media.Media` instances
                for this page.
            result_count
                The total number of media items for this query
            search_query
                The query the user searched for, if any

        """
        media = Media.query.published()

        media, show = helpers.filter_library_controls(media, show)

        if q:
            media = media.search(q, bool=True)

        if tag:
            tag = fetch_row(Tag, slug=tag)
            media = media.filter(Media.tags.contains(tag))

        return dict(
            media=media,
            result_count=media.count(),
            search_query=q,
            show=show,
            tag=tag,
        )
Пример #58
0
    def save_edit(self, id, body, **kwargs):
        """Save an edit from :class:`~mediacore.forms.admin.comments.EditCommentForm`.

        :param id: Comment ID
        :type id: ``int``
        :rtype: JSON dict
        :returns:
            success
                bool
            body
                The edited comment body after validation/filtering

        """
        comment = fetch_row(Comment, id)
        comment.body = body
        DBSession.add(comment)
        return dict(
            success=True,
            body=comment.body,
        )
Пример #59
0
    def edit(self, id, **kwargs):
        """Edit a single category.

        :param id: Category ID
        :rtype: Dict
        :returns:
            categories
                The list of :class:`~mediacore.model.categories.Category`
                instances for this page.
            category_form
                The :class:`~mediacore.forms.admin.settings.categories.CategoryForm` instance.

        """
        category = fetch_row(Category, id)

        return dict(
            category = category,
            category_form = category_form,
            category_row_form = category_row_form,
        )
Пример #60
0
    def rate(self, slug, up=None, down=None, **kwargs):
        """Say 'I like this' for the given media.

        :param slug: The media :attr:`~mediacore.model.media.Media.slug`
        :rtype: unicode
        :returns:
            The new number of likes

        """
        media = fetch_row(Media, slug=slug)

        if up:
            media.increment_likes()
        elif down:
            media.increment_dislikes()

        if request.is_xhr:
            return u''
        else:
            redirect(action='view')