Example #1
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,
                ),
            ),
        }
Example #3
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,
        )
Example #4
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,
        )
Example #5
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,
        )
Example #6
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,
        )
Example #7
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)
Example #8
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)
Example #9
0
    def _file_info(self, file, media):
        """Return a JSON-ready dict for the media file including links."""

        return dict(
            container = file.container,
            type = file.type,
            display_name = file.display_name,
            created = file.created_on.isoformat(),
            link = helpers.url_for(controller='/media', action='view',
                                   slug=media.slug, qualified=True),
            content = helpers.url_for(controller='/media', action='serve',
                                      id=file.id, container=file.container,
                                      slug=media.slug, qualified=True),
        )
Example #10
0
    def include(self):
        from mediacore.lib.helpers import url_for
        jquery = url_for('/scripts/third-party/jQuery-1.4.2-compressed.js', qualified=self.qualified)
        jwplayer = url_for('/scripts/third-party/jw_player/html5/jquery.jwplayer-compressed.js', qualified=self.qualified)
        skin = url_for('/scripts/third-party/jw_player/html5/skin/five.xml', qualified=self.qualified)
        include = """
<script type="text/javascript" src="%s"></script>
<script type="text/javascript" src="%s"></script>
<script type="text/javascript">
    jQuery('#%s').jwplayer({
        skin:'%s'
    });
</script>""" % (jquery, jwplayer, self.elem_id, skin)
        return include
Example #11
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,
        )
    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()
        )
Example #13
0
    def submit_async(self, **kwargs):
        """Ajax form validation and/or submission.

        This is the save handler for :class:`~mediacore.forms.media.UploadForm`.

        When ajax is enabled this action is called for each field as the user
        fills them in. Although the entire form is validated, the JS only
        provides the value of one field at a time,

        :param validate: A JSON list of field names to check for validation
        :parma \*\*kwargs: One or more form field values.
        :rtype: JSON dict
        :returns:
            :When validating one or more fields:

            valid
                bool
            err
                A dict of error messages keyed by the field names

            :When saving an upload:

            success
                bool
            redirect
                If valid, the redirect url for the upload successful page.

        """
        if "validate" in kwargs:
            # we're just validating the fields. no need to worry.
            fields = json.loads(kwargs["validate"])
            err = {}
            for field in fields:
                if field in tmpl_context.form_errors:
                    err[field] = tmpl_context.form_errors[field]

            data = dict(valid=len(err) == 0, err=err)
        else:
            # We're actually supposed to save the fields. Let's do it.
            if len(tmpl_context.form_errors) != 0:
                # if the form wasn't valid, return failure
                tmpl_context.form_errors["success"] = False
                data = tmpl_context.form_errors
            else:
                # else actually save it!
                kwargs.setdefault("name")

                media_obj = self.save_media_obj(
                    kwargs["name"],
                    kwargs["email"],
                    kwargs["title"],
                    kwargs["description"],
                    None,
                    kwargs["file"],
                    kwargs["url"],
                )
                email.send_media_notification(media_obj)
                data = dict(success=True, redirect=url_for(action="success"))

        return data
Example #14
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)
Example #15
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)
Example #16
0
def send_media_notification(media_obj):
    send_to = app_globals.settings['email_media_uploaded']
    if not send_to:
        # media notification emails are disabled!
        return

    edit_url = url_for(controller='/admin/media', action='edit',
                       id=media_obj.id, qualified=True),

    clean_description = strip_xhtml(line_break_xhtml(line_break_xhtml(media_obj.description)))

    type = media_obj.type
    title = media_obj.title
    author_name = media_obj.author.name
    author_email = media_obj.author.email
    subject = _('New %(type)s: %(title)s') % locals()
    body = _("""A new %(type)s file has been uploaded!

Title: %(title)s

Author: %(author_name)s (%(author_email)s)

Admin URL: %(edit_url)s

Description: %(clean_description)s
""") % locals()

    send(send_to, app_globals.settings['email_send_from'], subject, body)
Example #17
0
def send_media_notification(media_obj):
    send_to = app_globals.settings['email_media_uploaded']
    if not send_to:
        # media notification emails are disabled!
        return

    edit_url = url_for(controller='/admin/media', action='edit',
                       id=media_obj.id, qualified=True),

    clean_description = strip_xhtml(
            line_break_xhtml(line_break_xhtml(media_obj.description)))

    subject = 'New %s: %s' % (media_obj.type, media_obj.title)
    body = """A new %s file has been uploaded!

Title: %s

Author: %s (%s)

Admin URL: %s

Description: %s
""" % (media_obj.type, media_obj.title, media_obj.author.name,
       media_obj.author.email, edit_url, clean_description)

    send(send_to, app_globals.settings['email_send_from'], subject, body)
Example #18
0
    def login(self, came_from=None, **kwargs):
        if request.environ.get('repoze.who.identity'):
            redirect(came_from or '/')

        # the friendlyform plugin requires that these values are set in the
        # query string
        form_url = url_for('/login/submit',
                           came_from=(came_from or '').encode('utf-8'),
                           __logins=str(self._is_failed_login()))

        login_errors = None
        if self._is_failed_login():
            login_errors = Invalid('dummy',
                                   None, {},
                                   error_dict={
                                       '_form':
                                       Invalid(
                                           _('Invalid username or password.'),
                                           None, {}),
                                       'login':
                                       Invalid('dummy', None, {}),
                                       'password':
                                       Invalid('dummy', None, {}),
                                   })
        return dict(
            login_form=login_form,
            form_action=form_url,
            form_values=kwargs,
            login_errors=login_errors,
        )
Example #19
0
def send_media_notification(media_obj):
    send_to = fetch_setting("email_media_uploaded")
    if not send_to:
        # media notification emails are disabled!
        return

    edit_url = (url_for(controller="mediaadmin", action="edit", id=media_obj.id, qualified=True),)

    clean_description = strip_xhtml(line_break_xhtml(line_break_xhtml(media_obj.description)))

    subject = "New %s: %s" % (media_obj.type, media_obj.title)
    body = """A new %s file has been uploaded!

Title: %s

Author: %s (%s)

Admin URL: %s

Description: %s
""" % (
        media_obj.type,
        media_obj.title,
        media_obj.author.name,
        media_obj.author.email,
        edit_url,
        clean_description,
    )

    send(send_to, fetch_setting("email_send_from"), subject, body)
    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,
        )
Example #21
0
class YouTubeImportController(BaseSettingsController):
    @expose('youtube_import/admin/import.html')
    def index(self, **kwargs):
        category_tree = Category.query.order_by(Category.name).populated_tree()
        return dict(
            form=import_form,
            form_values=kwargs,
            form_action=url_for(controller='youtube_import',
                                action='perform_import'),
            category_tree=category_tree,
        )

    @expose()
    @validate(import_form, error_handler=index)
    @autocommit
    def perform_import(self, youtube, **kwargs):
        auto_publish = youtube.get('auto_publish', False)
        user = request.perm.user
        tags = kwargs.get('youtube.tags')
        categories = kwargs.get('youtube.categories')

        channel_names = parse_channel_names(youtube.get('channel_names', ''))
        importer = YouTubeImporter(user, auto_publish, tags, categories)
        try:
            for channel_name in channel_names:
                importer.import_videos_from_channel(channel_name)
        except YouTubeQuotaExceeded, e:
            error_message = e.args[0]
            c.form_errors['_the_form'] = error_message
            return self.index(youtube=youtube, **kwargs)

        # Redirect to the Media view page, when the import is complete
        redirect(url_for(controller='admin/media', action='index'))
Example #22
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,
        }
Example #23
0
    def submit_async(self, **kwargs):
        """Ajax form validation and/or submission.

        This is the save handler for :class:`~mediacore.forms.media.UploadForm`.

        When ajax is enabled this action is called for each field as the user
        fills them in. Although the entire form is validated, the JS only
        provides the value of one field at a time,

        :param validate: A JSON list of field names to check for validation
        :parma \*\*kwargs: One or more form field values.
        :rtype: JSON dict
        :returns:
            :When validating one or more fields:

            valid
                bool
            err
                A dict of error messages keyed by the field names

            :When saving an upload:

            success
                bool
            redirect
                If valid, the redirect url for the upload successful page.

        """
        if 'validate' in kwargs:
            # we're just validating the fields. no need to worry.
            fields = json.loads(kwargs['validate'])
            err = {}
            for field in fields:
                if field in tmpl_context.form_errors:
                    err[field] = tmpl_context.form_errors[field]

            data = dict(valid=len(err) == 0, err=err)
        else:
            # We're actually supposed to save the fields. Let's do it.
            if len(tmpl_context.form_errors) != 0:
                # if the form wasn't valid, return failure
                tmpl_context.form_errors['success'] = False
                data = tmpl_context.form_errors
            else:
                # else actually save it!
                kwargs.setdefault('name')

                media_obj = self.save_media_obj(
                    kwargs['name'],
                    kwargs['email'],
                    kwargs['title'],
                    kwargs['description'],
                    None,
                    kwargs['file'],
                    kwargs['url'],
                )
                email.send_media_notification(media_obj)
                data = dict(success=True, redirect=url_for(action='success'))

        return data
Example #24
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,
        }
Example #25
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,
        )
Example #26
0
def send_comment_notification(media_obj, comment):
    """
    Helper method to send a email notification that a comment has been posted.

    Sends to the address configured in the 'email_comment_posted' setting,
    if it is configured.

    :param media_obj: The media object to send a notification about.
    :type media_obj: :class:`~mediacore.model.media.Media` instance

    :param comment: The newly posted comment.
    :type comment: :class:`~mediacore.model.comments.Comment` instance
    """
    send_to = request.settings['email_comment_posted']
    if not send_to:
        # Comment notification emails are disabled!
        return

    author_name = media_obj.author.name
    comment_subject = comment.subject
    post_url = url_for(controller='/media', action='view', slug=media_obj.slug, qualified=True)
    comment_body = strip_xhtml(line_break_xhtml(line_break_xhtml(comment.body)))
    subject = _('New Comment: %(comment_subject)s') % locals()
    body = _("""A new comment has been posted!

Author: %(author_name)s
Post: %(post_url)s

Body: %(comment_body)s
""") % locals()

    send(send_to, request.settings['email_send_from'], subject, body)
Example #27
0
    def post_login(self, came_from=url_for(controller='admin', action='index'), **kwargs):
        if not request.identity:
            login_counter = request.environ['repoze.who.logins'] + 1
            redirect(came_from)

        userid = request.identity['repoze.who.userid']
        redirect(came_from)
Example #28
0
    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:
            permission_ids = map(lambda permission: permission.permission_id,
                                 group.permissions)
            group_values = dict(display_name=group.display_name,
                                group_name=group.group_name,
                                permissions=permission_ids)

        return dict(
            group=group,
            group_form=group_form,
            group_action=url_for(action='save'),
            group_values=group_values,
        )
Example #29
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')
Example #30
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)
Example #31
0
    def include(self):
        if self.media.type != VIDEO:
            return ''
        from mediacore.lib.helpers import url_for
        js = url_for('/scripts/third-party/zencoder-video-js/video-yui-compressed.js', qualified=self.qualified)
        css = url_for('/scripts/third-party/zencoder-video-js/video-js.css', qualified=self.qualified)
        include = """
<script type="text/javascript" src="%s"></script>
<link rel="stylesheet" href="%s" type="text/css" media="screen" />
<script type="text/javascript">
    window.addEvent('domready', function(){
        var media = $('%s');
        var wrapper = new Element('div', {'class': 'video-js-box'}).wraps(media);
        var vjs = new VideoJS(media);
    });
</script>""" % (js, css, self.elem_id)
        return include
Example #32
0
 def post_login(self, came_from=None, **kwargs):
     if not request.identity:
         # The FriendlyForm plugin will always issue a redirect to 
         # /login/continue (post login url) even for failed logins.
         # If 'came_from' is a protected page (i.e. /admin) we could just 
         # redirect there and the login form will be displayed again with
         # our login error message.
         # However if the user tried to login from the front page, this 
         # mechanism doesn't work so go to the login method directly here.
         self._increase_number_of_failed_logins()
         return self.login(came_from=came_from)
     if came_from:
         redirect(came_from)
     # It is important to return absolute URLs (if app mounted in subdirectory)
     if request.perm.contains_permission(u'edit') or request.perm.contains_permission(u'admin'):
         redirect(url_for('/admin', qualified=True))
     redirect(url_for('/', qualified=True))
Example #33
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 index(self, **kwargs):
     category_tree = Category.query.order_by(Category.name).populated_tree()
     return dict(
         form = import_form,
         form_values=kwargs, 
         form_action=url_for(controller='youtube_import', action='perform_import'),
         category_tree = category_tree,
     )
Example #35
0
    def google(self, page=None, limit=10000, **kwargs):
        """Generate a sitemap which contains googles Video Sitemap information.

        This action may return a <sitemapindex> or a <urlset>, depending
        on how many media items are in the database, and the values of the
        page and limit params.

        :param page: Page number, defaults to 1.
        :type page: int
        :param page: max records to display on page, defaults to 10000.
        :type page: int

        """
        if request.settings['sitemaps_display'] != 'True':
            abort(404)

        response.content_type = mimeparse.best_match(
            ['application/xml', 'text/xml'],
            request.environ.get('HTTP_ACCEPT', '*/*')
        )

        media = Media.query.published()

        if page is None:
            if media.count() > limit:
                return dict(pages=math.ceil(media.count() / float(limit)))
        else:
            page = int(page)
            media = media.offset(page * limit).limit(limit)

        if page:
            links = []
        else:
            links = [
                url_for(controller='/', qualified=True),
                url_for(controller='/media', show='popular', qualified=True),
                url_for(controller='/media', show='latest', qualified=True),
                url_for(controller='/categories', qualified=True),
            ]

        return dict(
            media = media,
            page = page,
            links = links,
        )
    def google(self, page=None, limit=10000, **kwargs):
        """Generate a sitemap which contains googles Video Sitemap information.

        This action may return a <sitemapindex> or a <urlset>, depending
        on how many media items are in the database, and the values of the
        page and limit params.

        :param page: Page number, defaults to 1.
        :type page: int
        :param page: max records to display on page, defaults to 10000.
        :type page: int

        """
        if request.settings['sitemaps_display'] != 'True':
            abort(404)

        response.content_type = mimeparse.best_match(
            ['application/xml', 'text/xml'],
            request.environ.get('HTTP_ACCEPT', '*/*')
        )

        media = Media.query.published()

        if page is None:
            if media.count() > limit:
                return dict(pages=math.ceil(media.count() / float(limit)))
        else:
            page = int(page)
            media = media.offset(page * limit).limit(limit)

        if page:
            links = []
        else:
            links = [
                url_for(controller='/', qualified=True),
                url_for(controller='/media', show='popular', qualified=True),
                url_for(controller='/media', show='latest', qualified=True),
                url_for(controller='/categories', qualified=True),
            ]

        return dict(
            media = media,
            page = page,
            links = links,
        )
Example #37
0
 def index(self, **kwargs):
     category_tree = Category.query.order_by(Category.name).populated_tree()
     return dict(
         form=import_form,
         form_values=kwargs,
         form_action=url_for(controller='youtube_import',
                             action='perform_import'),
         category_tree=category_tree,
     )
Example #38
0
 def post_login(self, came_from=None, **kwargs):
     if not request.identity:
         # The FriendlyForm plugin will always issue a redirect to
         # /login/continue (post login url) even for failed logins.
         # If 'came_from' is a protected page (i.e. /admin) we could just
         # redirect there and the login form will be displayed again with
         # our login error message.
         # However if the user tried to login from the front page, this
         # mechanism doesn't work so go to the login method directly here.
         self._increase_number_of_failed_logins()
         return self.login(came_from=came_from)
     if came_from:
         redirect(came_from)
     # It is important to return absolute URLs (if app mounted in subdirectory)
     if request.perm.contains_permission(
             u'edit') or request.perm.contains_permission(u'admin'):
         redirect(url_for('/admin', qualified=True))
     redirect(url_for('/', qualified=True))
Example #39
0
    def explore(self, **kwargs):
        """Display the most recent 15 media.

        :rtype: Dict
        :returns:
            latest
                Latest media
            popular
                Latest media

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

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())

        featured = None
        featured_cat = helpers.get_featured_category()
        if featured_cat:
            featured = viewable_media(latest.in_category(featured_cat)).first()
        if not featured:
            featured = viewable_media(popular).first()

        latest = viewable_media(latest.exclude(featured))[:8]
        popular = viewable_media(popular.exclude(featured, latest))[:5]
        if request.settings['sitemaps_display'] == 'True':
            response.feed_links.extend([
                (url_for(controller='/sitemaps',
                         action='google'), u'Sitemap XML'),
                (url_for(controller='/sitemaps',
                         action='mrss'), u'Sitemap RSS'),
            ])
        if request.settings['rss_display'] == 'True':
            response.feed_links.extend([
                (url_for(controller='/sitemaps',
                         action='latest'), u'Latest RSS'),
            ])

        return dict(
            featured=featured,
            latest=latest,
            popular=popular,
            categories=Category.query.populated_tree(),
        )
Example #40
0
    def login(self, came_from=url_for(controller='admin', action='index'), **kwargs):
        login_counter = request.environ.get('repoze.who.logins', 0)
        if login_counter > 0:
            # TODO: display a 'wrong username/password' warning
            pass

        return dict(
            login_counter = str(login_counter),
            came_from = came_from,
        )
Example #41
0
def add_settings_link():
    """Generate new links for the admin Settings menu.

    The :data:`mediacore.plugin.events.plugin_settings_links` event
    is defined as a :class:`mediacore.plugin.events.GeneratorEvent`
    which expects all observers to yield their content for use.

    """
    yield (_('Search Engine Optimization', domain='mediacore_seo'),
           url_for(controller='/seo/admin/settings'))
Example #42
0
def add_settings_link():
    """Generate new links for the admin Settings menu.

    The :data:`mediacore.plugin.events.plugin_settings_links` event
    is defined as a :class:`mediacore.plugin.events.GeneratorEvent`
    which expects all observers to yield their content for use.

    """
    yield (_('Search Engine Optimization', domain='mediacore_seo'),
           url_for(controller='/seo/admin/settings'))
Example #43
0
    def _info(self, media, podcast_slugs=None, include_embed=False):
        """Return a JSON-ready dict for the given media instance"""
        if media.podcast_id:
            media_url = url_for(controller='/media', action='view', slug=media.slug,
                                podcast_slug=media.podcast.slug, qualified=True)
        else:
            media_url = url_for(controller="/media", action="view", slug=media.slug,
                                qualified=True)

        if media.podcast_id is None:
            podcast_slug = None
        elif podcast_slugs:
            podcast_slug = podcast_slugs[media.podcast_id]
        else:
            podcast_slug = DBSession.query(Podcast.slug)\
                .filter_by(id=media.podcast_id).scalar()

        thumbs = {}
        for size in config['thumb_sizes'][media._thumb_dir].iterkeys():
            thumbs[size] = thumb(media, size, qualified=True)

        info = dict(
            id = media.id,
            slug = media.slug,
            url = media_url,
            title = media.title,
            author = media.author.name,
            type = media.type,
            podcast = podcast_slug,
            description = media.description,
            description_plain = media.description_plain,
            comment_count = media.comment_count_published,
            publish_on = unicode(media.publish_on),
            likes = media.likes,
            views = media.views,
            thumbs = thumbs,
            categories = dict((c.slug, c.name) for c in list(media.categories)),
        )

        if include_embed:
            info['embed'] = unicode(helpers.embed_player(media))

        return info
Example #44
0
    def update_status(self, id, update_button=None, publish_on=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``
        :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 update_button == _('Review Complete'):
            media.reviewed = True
        elif update_button == _('Publish Now'):
            media.publishable = True
            media.publish_on = publish_on or datetime.now()
            media.update_popularity()
            # Remove the stub prefix if the user wants the default media title
            if media.slug.startswith('_stub_'):
                new_slug = get_available_slug(Media, media.slug[len('_stub_'):])
                media.slug = new_slug
        elif publish_on:
            media.publish_on = publish_on
            media.update_popularity()

        # 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')
Example #45
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')
Example #46
0
    def explore(self, **kwargs):
        """Display the most recent 15 media.

        :rtype: Dict
        :returns:
            latest
                Latest media
            popular
                Latest media

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

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())

        featured = None
        featured_cat = helpers.get_featured_category()
        if featured_cat:
            featured = viewable_media(latest.in_category(featured_cat)).first()
        if not featured:
            featured = viewable_media(popular).first()

        latest = viewable_media(latest.exclude(featured))[:8]
        popular = viewable_media(popular.exclude(featured, latest))[:5]
        if request.settings['sitemaps_display'] == 'True':
            response.feed_links.extend([
                (url_for(controller='/sitemaps', action='google'), u'Sitemap XML'),
                (url_for(controller='/sitemaps', action='mrss'), u'Sitemap RSS'),
            ])
        if request.settings['rss_display'] == 'True':
            response.feed_links.extend([
                (url_for(controller='/sitemaps', action='latest'), u'Latest RSS'),
            ])

        return dict(
            featured = featured,
            latest = latest,
            popular = popular,
            categories = Category.query.populated_tree(),
        )
Example #47
0
    def login(self, came_from=None, **kwargs):
        login_counter = request.environ.get('repoze.who.logins', 0)
        if login_counter > 0:
            # TODO: display a 'wrong username/password' warning
            pass

        if not came_from:
            came_from = url_for(controller='admin', action='index', qualified=True)

        return dict(
            login_counter = str(login_counter),
            came_from = came_from,
        )
Example #48
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:

        """
        engine = self.fetch_engine(id, engine_type)

        return {
            'engine': engine,
            'form': engine.settings_form,
            'form_action': url_for(action='save', engine_type=engine_type),
            'form_values': kwargs,
        }
Example #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,
        }
Example #50
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,
        )
Example #51
0
def send_media_notification(media_obj):
    """
    Send a creation notification email that a new Media object has been
    created.

    Sends to the address configured in the 'email_media_uploaded' address,
    if one has been created.

    :param media_obj: The media object to send a notification about.
    :type media_obj: :class:`~mediacore.model.media.Media` instance
    """
    send_to = request.settings['email_media_uploaded']
    if not send_to:
        # media notification emails are disabled!
        return

    edit_url = url_for(controller='/admin/media',
                       action='edit',
                       id=media_obj.id,
                       qualified=True)

    clean_description = strip_xhtml(
        line_break_xhtml(line_break_xhtml(media_obj.description)))

    type = media_obj.type
    title = media_obj.title
    author_name = media_obj.author.name
    author_email = media_obj.author.email
    subject = _('New %(type)s: %(title)s') % locals()
    body = _("""A new %(type)s file has been uploaded!

Title: %(title)s

Author: %(author_name)s (%(author_email)s)

Admin URL: %(edit_url)s

Description: %(clean_description)s
""") % locals()

    send(send_to, request.settings['email_send_from'], subject, body)
Example #52
0
    def index(self, slug=None, **kwargs):
        media = Media.query.published()

        if c.category:
            media = media.in_category(c.category)

            response.feed_links.append(
                (url_for(controller='/categories',
                         action='feed',
                         slug=c.category.slug),
                 _('Latest media in %s') % c.category.name))

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())

        latest = viewable_media(latest)[:5]
        popular = viewable_media(popular.exclude(latest))[:5]

        return dict(
            latest=latest,
            popular=popular,
        )
Example #53
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)

        if request.settings['comments_engine'] != 'mediacore':
            abort(404)
        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)
Example #54
0
from mediacore.forms.uploader import UploadForm
from mediacore.lib import email
from mediacore.lib.base import BaseController
from mediacore.lib.decorators import (autocommit, expose, expose_xhr,
                                      observable, paginate, validate)
from mediacore.lib.helpers import redirect, url_for
from mediacore.lib.storage import add_new_media_file
from mediacore.lib.thumbnails import create_default_thumbs_for, has_thumbs
from mediacore.model import Author, DBSession, get_available_slug, Media
from mediacore.plugin import events

import logging
log = logging.getLogger(__name__)

upload_form = UploadForm(action=url_for(controller='/upload', action='submit'),
                         async_action=url_for(controller='/upload',
                                              action='submit_async'))


class UploadController(BaseController):
    """
    Media Upload Controller
    """
    def __before__(self, *args, **kwargs):
        if not request.settings['appearance_enable_user_uploads']:
            abort(404)
        return BaseController.__before__(self, *args, **kwargs)

    @expose('upload/index.html')
    @observable(events.UploadController.index)
def add_settings_link():
    yield (_('YouTube Import', domain=gettext_domain),
           url_for(controller='/admin/plugins/youtube_import'))
Example #56
0
    def _info(self, media, podcast_slugs=None, include_embed=False):
        """
        Return a **media_info** dict--a JSON-ready dict for describing a media instance.

        :rtype: JSON-ready dict
        :returns: The returned dict has the following fields:

            author (unicode)
                The name of the
                :attr:`author <mediacore.model.media.Media.author>` of the
                media instance.
            categories (dict of unicode)
                A JSON-ready dict representing the categories the media
                instance is in. Keys are the unique
                :attr:`slugs <mediacore.model.podcasts.Podcast.slug>`
                for each category, values are the human-readable
                :attr:`title <mediacore.model.podcasts.podcast.Title>`
                of that category.
            id (int)
                The numeric unique :attr:`id <mediacore.model.media.Media.id>` of
                the media instance.
            slug (unicode)
                The more human readable unique identifier
                (:attr:`slug <mediacore.model.media.Media.slug>`)
                of the media instance.
            url (unicode)
                A permalink (HTTP) to the MediaCore view page for the media instance.
            embed (unicode)
                HTML code that can be used to embed the video in another site.
            title (unicode)
                The :attr:`title <mediacore.model.media.Media.title>` of
                the media instance.
            type (string, one of ['%s', '%s'])
                The :attr:`type <mediacore.model.media.Media.type>` of
                the media instance
            podcast (unicode or None)
                The :attr:`slug <mediacore.model.podcasts.Podcast.slug>` of the
                :class:`podcast <mediacore.model.podcasts.Podcast>` that
                the media instance has been published under, or None
            description (unicode)
                An XHTML
                :attr:`description <mediacore.model.media.Media.description>`
                of the media instance.
            description_plain (unicode)
                A plain text
                :attr:`description <mediacore.model.media.Media.description_plain>`
                of the media instance.
            comment_count (int)
                The number of published comments on the media instance.
            publish_on (unicode)
                The date of publishing in "YYYY-MM-DD HH:MM:SS" (ISO 8601) format.
                e.g.  "2010-02-16 15:06:49"
            likes (int)
                The number of :attr:`like votes <mediacore.model.media.Media.likes>`
                that the media instance has received.
            views (int)
                The number of :attr:`views <mediacore.model.media.Media.views>`
                that the media instance has received.
            thumbs (dict)
                A dict of dicts containing URLs, width and height of
                different sizes of thumbnails. The default sizes
                are 's', 'm' and 'l'. Using medium for example::

                    medium_url = thumbs['m']['url']
                    medium_width = thumbs['m']['x']
                    medium_height = thumbs['m']['y']
        """
        if media.podcast_id:
            media_url = url_for(controller='/media',
                                action='view',
                                slug=media.slug,
                                podcast_slug=media.podcast.slug,
                                qualified=True)
        else:
            media_url = url_for_media(media, qualified=True)

        if media.podcast_id is None:
            podcast_slug = None
        elif podcast_slugs:
            podcast_slug = podcast_slugs[media.podcast_id]
        else:
            podcast_slug = DBSession.query(Podcast.slug)\
                .filter_by(id=media.podcast_id).scalar()

        thumbs = {}
        for size in config['thumb_sizes'][media._thumb_dir].iterkeys():
            thumbs[size] = thumb(media, size, qualified=True)

        info = dict(
            id=media.id,
            slug=media.slug,
            url=media_url,
            title=media.title,
            author=media.author.name,
            type=media.type,
            podcast=podcast_slug,
            description=media.description,
            description_plain=media.description_plain,
            comment_count=media.comment_count_published,
            publish_on=unicode(media.publish_on),
            likes=media.likes,
            views=media.views,
            thumbs=thumbs,
            categories=dict((c.slug, c.name) for c in list(media.categories)),
        )

        if include_embed:
            info['embed'] = unicode(helpers.embed_player(media))

        return info
Example #57
0
class MediaController(BaseController):
    allow_only = has_permission('edit')

    @expose_xhr('admin/media/index.html', 'admin/media/index-table.html')
    @paginate('media', items_per_page=15)
    @observable(events.Admin.MediaController.index)
    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,
        )


    @expose('admin/media/edit.html')
    @validate(validators={'podcast': validators.Int()})
    @autocommit
    @observable(events.Admin.MediaController.edit)
    def edit(self, id, **kwargs):
        """Display the media forms for editing or adding.

        This page serves as the error_handler for every kind of edit action,
        if anything goes wrong with them they'll be redirected here.

        :param id: Media ID
        :type id: ``int`` or ``"new"``
        :param \*\*kwargs: Extra args populate the form for ``"new"`` media
        :returns:
            media
                :class:`~mediacore.model.media.Media` instance
            media_form
                The :class:`~mediacore.forms.admin.media.MediaForm` instance
            media_action
                ``str`` form submit url
            media_values
                ``dict`` form values
            file_add_form
                The :class:`~mediacore.forms.admin.media.AddFileForm` instance
            file_add_action
                ``str`` form submit url
            file_edit_form
                The :class:`~mediacore.forms.admin.media.EditFileForm` instance
            file_edit_action
                ``str`` form submit url
            thumb_form
                The :class:`~mediacore.forms.admin.ThumbForm` instance
            thumb_action
                ``str`` form submit url
            update_status_form
                The :class:`~mediacore.forms.admin.media.UpdateStatusForm` instance
            update_status_action
                ``str`` form submit url

        """
        media = fetch_row(Media, id)

        if tmpl_context.action == 'save' or id == 'new':
            # Use the values from error_handler or GET for new podcast media
            media_values = kwargs
            user = request.environ['repoze.who.identity']['user']
            media_values.setdefault('author_name', user.display_name)
            media_values.setdefault('author_email', user.email_address)
        else:
            # Pull the defaults from the media item
            media_values = dict(
                podcast = media.podcast_id,
                slug = media.slug,
                title = media.title,
                author_name = media.author.name,
                author_email = media.author.email,
                description = media.description,
                tags = ', '.join((tag.name for tag in media.tags)),
                categories = [category.id for category in media.categories],
                notes = media.notes,
            )

        # Re-verify the state of our Media object in case the data is nonsensical
        if id != 'new':
            media.update_status()

        return dict(
            media = media,
            media_form = media_form,
            media_action = url_for(action='save'),
            media_values = media_values,
            category_tree = Category.query.order_by(Category.name).populated_tree(),
            file_add_form = add_file_form,
            file_add_action = url_for(action='add_file'),
            file_edit_form = edit_file_form,
            file_edit_action = url_for(action='edit_file'),
            thumb_form = thumb_form,
            thumb_action = url_for(action='save_thumb'),
            update_status_form = update_status_form,
            update_status_action = url_for(action='update_status'),
        )

    @expose_xhr(request_method='POST')
    @validate_xhr(media_form, error_handler=edit)
    @autocommit
    @observable(events.Admin.MediaController.save)
    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)


    @expose('json', request_method='POST')
    @validate(add_file_form)
    @autocommit
    @observable(events.Admin.MediaController.add_file)
    def add_file(self, id, file=None, url=None, **kwargs):
        """Save action for the :class:`~mediacore.forms.admin.media.AddFileForm`.

        Creates a new :class:`~mediacore.model.media.MediaFile` from the
        uploaded file or the local or remote URL.

        :param id: Media ID. If ``"new"`` a new Media stub is created.
        :type id: :class:`int` or ``"new"``
        :param file: The uploaded file
        :type file: :class:`cgi.FieldStorage` or ``None``
        :param url: A URL to a recognizable audio or video file
        :type url: :class:`unicode` or ``None``
        :rtype: JSON dict
        :returns:
            success
                bool
            message
                Error message, if unsuccessful
            media_id
                The :attr:`~mediacore.model.media.Media.id` which is
                important if new media has just been created.
            file_id
                The :attr:`~mediacore.model.media.MediaFile.id` for the newly
                created file.
            edit_form
                The rendered XHTML :class:`~mediacore.forms.admin.media.EditFileForm`
                for this file.
            status_form
                The rendered XHTML :class:`~mediacore.forms.admin.media.UpdateStatusForm`

        """
        if id == 'new':
            media = Media()
            user = request.environ['repoze.who.identity']['user']
            media.author = Author(user.display_name, user.email_address)
            # Create a temp stub until we can set it to something meaningful
            timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            media.title = u'Temporary stub %s' % timestamp
            media.slug = get_available_slug(Media, '_stub_' + timestamp)
            media.reviewed = True
            DBSession.add(media)
            DBSession.flush()
        else:
            media = fetch_row(Media, id)

        media_file = add_new_media_file(media, file, url)
        if media.slug.startswith('_stub_'):
            media.title = media_file.display_name
            media.slug = get_available_slug(Media, '_stub_' + media.title)

        # The thumbs may have been created already by add_new_media_file
        if id == 'new' and not has_thumbs(media):
            create_default_thumbs_for(media)

        media.update_status()

        # Render some widgets so the XHTML can be injected into the page
        edit_form_xhtml = unicode(edit_file_form.display(
            action=url_for(action='edit_file', id=media.id),
            file=media_file))
        status_form_xhtml = unicode(update_status_form.display(
            action=url_for(action='update_status', id=media.id),
            media=media))

        data = dict(
            success = True,
            media_id = media.id,
            file_id = media_file.id,
            file_type = media_file.type,
            edit_form = edit_form_xhtml,
            status_form = status_form_xhtml,
            title = media.title,
            slug = media.slug,
            description = media.description,
            link = url_for(action='edit', id=media.id),
            duration = helpers.duration_from_seconds(media.duration),
        )

        return data


    @expose('json', request_method='POST')
    @autocommit
    @observable(events.Admin.MediaController.edit_file)
    def edit_file(self, id, file_id, file_type=None, duration=None, delete=None, bitrate=None, width_height=None, **kwargs):
        """Save action for the :class:`~mediacore.forms.admin.media.EditFileForm`.

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

        XXX: We do NOT use the @validate decorator due to complications with
             partial validation. The JS sends only the value it wishes to
             change, so we only want to validate that one value.
             FancyValidator.if_missing seems to eat empty values and assign
             them None, but there's an important difference to us between
             None (no value from the user) and an empty value (the user
             is clearing the value of a field).

        :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)
        file_id = int(file_id) # Just in case validation failed somewhere.

        for file in media.files:
            if file.id == file_id:
                break
        else:
            file = None

        fields = edit_file_form.c
        try:
            if file is None:
                data['message'] = _('File "%s" does not exist.') % file_id
            elif file_type:
                file.type = fields.file_type.validate(file_type)
                data['success'] = True
            elif duration is not None:
                media.duration = duration = fields.duration.validate(duration)
                data['success'] = True
                data['duration'] = helpers.duration_from_seconds(media.duration)
            elif width_height is not None:
                width_height = fields.width_height.validate(width_height)
                file.width, file.height = width_height or (0, 0)
                data['success'] = True
            elif bitrate is not None:
                file.bitrate = fields.bitrate.validate(bitrate)
                data['success'] = True
            elif delete:
                file.storage.delete(file.unique_id)
                DBSession.delete(file)
                DBSession.flush()
                media = fetch_row(Media, id)
                data['success'] = True
            else:
                data['message'] = _('No action to perform.')
        except Invalid, e:
            data['success'] = False
            data['message'] = unicode(e)

        if data['success']:
            data['file_type'] = file.type
            media.update_status()
            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
Example #58
0
    def merge_stubs(self, orig_id, input_id, **kwargs):
        """Merge in a newly created media item.

        This is merges media that has just been created. It must have:
            1. a non-default thumbnail, or
            2. a file, or
            3. a title, description, etc

        :param orig_id: Media ID to copy data to
        :type orig_id: ``int``
        :param input_id: Media ID to source files, thumbs, etc from
        :type input_id: ``int``
        :returns: JSON dict

        """
        orig = fetch_row(Media, orig_id)
        input = fetch_row(Media, input_id)
        merged_files = []

        # Merge in the file(s) from the input stub
        if input.slug.startswith('_stub_') and input.files:
            for file in input.files[:]:
                # XXX: The filename will still use the old ID
                file.media = orig
                merged_files.append(file)
            DBSession.delete(input)

        # The original is a file or thumb stub, copy in the new values
        elif orig.slug.startswith('_stub_') \
        and not input.slug.startswith('_stub_'):
            DBSession.delete(input)
            DBSession.flush()
            orig.podcast = input.podcast
            orig.title = input.title
            orig.subtitle = input.subtitle
            orig.slug = input.slug
            orig.author = input.author
            orig.description = input.description
            orig.notes = input.notes
            orig.duration = input.duration
            orig.views = input.views
            orig.likes = input.likes
            orig.publish_on = input.publish_on
            orig.publish_until = input.publish_until
            orig.categories = input.categories
            orig.tags = input.tags
            orig.update_popularity()

        # Copy the input thumb over the default thumbnail
        elif input.slug.startswith('_stub_') \
        and has_default_thumbs(orig) \
        and not has_default_thumbs(input):
            for key, dst_path in thumb_paths(orig).iteritems():
                src_path = thumb_path(input, key)
                # This will raise an OSError on Windows, but not *nix
                os.rename(src_path, dst_path)
            DBSession.delete(input)

        # Report an error
        else:
            return dict(
                success = False,
                message = u'No merge operation fits.',
            )

        orig.update_status()

        status_form_xhtml = unicode(update_status_form.display(
            action=url_for(action='update_status', id=orig.id),
            media=orig))

        file_xhtml = {}
        for file in merged_files:
            file_xhtml[file.id] = unicode(edit_file_form.display(
                action=url_for(action='edit_file', id=orig.id),
                file=file))

        return dict(
            success = True,
            media_id = orig.id,
            title = orig.title,
            link = url_for(action='edit', id=orig.id),
            status_form = status_form_xhtml,
            file_forms = file_xhtml,
        )