def setUp(self):
     super(LimitFeedItemsValidatorTest, self).setUp()
     self.init_fake_request()
     # just to be sure all settings have been cleared.
     assert_none(app_globals.settings.get('default_feed_results'))
     app_globals.settings['default_feed_results'] = 42
     self.validator = LimitFeedItemsValidator()
class LimitFeedItemsValidatorTest(DBTestCase, RequestMixin):
    def setUp(self):
        super(LimitFeedItemsValidatorTest, self).setUp()
        self.init_fake_request()
        # just to be sure all settings have been cleared.
        assert_none(app_globals.settings.get('default_feed_results'))
        app_globals.settings['default_feed_results'] = 42
        self.validator = LimitFeedItemsValidator()
    
    def tearDown(self):
        self.remove_globals()
        super(LimitFeedItemsValidatorTest, self).tearDown()
    
    def to_python(self, value):
        return self.validator.to_python(value)
    
    def test_specified_value_overrides_default(self):
        assert_equals(12, self.to_python('12'))
    
    def test_returns_default_for_empty_items(self):
        assert_equals(42, self.to_python(''))
        assert_equals(42, self.to_python(None))
    
    def test_can_return_unlimited_items(self):
        app_globals.settings['default_feed_results'] = ''
        assert_none(self.to_python(''))
        
        app_globals.settings['default_feed_results'] = '-1'
        assert_none(self.to_python(''))
    
    def test_ignores_missing_setting(self):
        del app_globals.settings['default_feed_results']
        assert_equals(30, self.to_python(''))
    
    def test_returns_default_number_for_invalid_input(self):
        assert_equals(42, self.to_python('invalid'))
        assert_equals(42, self.to_python(-1))
    
    def test_returns_default_for_missing_items(self):
        schema = Schema()
        schema.add_field('limit', self.validator)
        assert_equals(dict(limit=42), schema.to_python({}))
示例#3
0
class PodcastsController(BaseController):
    """
    Podcast Series Controller

    This handles episode collections, individual episodes are handled as
    regular media by :mod:`mediadrop.controllers.media`.
    """
    @expose('podcasts/index.html')
    @observable(events.PodcastsController.index)
    def index(self, **kwargs):
        """List podcasts and podcast media.

        :rtype: dict
        :returns:
            podcasts
                The :class:`~mediadrop.model.podcasts.Podcast` instance

        """
        podcasts = Podcast.query\
            .options(orm.undefer('media_count_published'))\
            .all()

        if len(podcasts) == 1:
            redirect(action='view', slug=podcasts[0].slug)

        podcast_episodes = {}
        for podcast in podcasts:
            episode_query = podcast.media.published().order_by(
                Media.publish_on.desc())
            podcast_episodes[podcast] = viewable_media(episode_query)[:4]

        return dict(
            podcasts=podcasts,
            podcast_episodes=podcast_episodes,
        )

    @expose('podcasts/view.html')
    @paginate('episodes', items_per_page=10)
    @observable(events.PodcastsController.view)
    def view(self, slug, page=1, show='latest', **kwargs):
        """View a podcast and the media that belongs to it.

        :param slug: A :attr:`~mediadrop.model.podcasts.Podcast.slug`
        :param page: Page number, defaults to 1.
        :type page: int
        :rtype: dict
        :returns:
            podcast
                A :class:`~mediadrop.model.podcasts.Podcast` instance.
            episodes
                A list of :class:`~mediadrop.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,
        )

    @validate(validators={'limit': LimitFeedItemsValidator()})
    @beaker_cache(expire=60 * 20)
    @expose('podcasts/feed.xml')
    @observable(events.PodcastsController.feed)
    def feed(self, slug, limit=None, **kwargs):
        """Serve the feed as RSS 2.0.

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

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

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

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

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

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

        episode_query = podcast.media.published().order_by(
            Media.publish_on.desc())
        episodes = viewable_media(episode_query)
        if limit is not None:
            episodes = episodes.limit(limit)

        return dict(
            podcast=podcast,
            episodes=episodes,
        )
示例#4
0
class SitemapsController(BaseController):
    """
    Sitemap generation
    """

    @validate(validators={
        'page': validators.Int(if_empty=None, if_missing=None, if_invalid=None), 
        'limit': validators.Int(if_empty=10000, if_missing=10000, if_invalid=10000)
    })
    @beaker_cache(expire=60 * 60 * 4)
    @expose('sitemaps/google.xml')
    @observable(events.SitemapsController.google)
    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 = \
            content_type_for_response(['application/xml', 'text/xml'])

        media = viewable_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,
        )

    @beaker_cache(expire=60 * 60, query_args=True)
    @expose('sitemaps/mrss.xml')
    @observable(events.SitemapsController.mrss)
    def mrss(self, **kwargs):
        """Generate a media rss (mRSS) feed of all the sites media."""
        if request.settings['sitemaps_display'] != 'True':
            abort(404)


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

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

        return dict(
            media = media,
            title = 'MediaRSS Sitemap',
        )

    @validate(validators={
        'limit': LimitFeedItemsValidator(),
        'skip': validators.Int(if_empty=0, if_missing=0, if_invalid=0)
    })
    @beaker_cache(expire=60 * 3)
    @expose('sitemaps/mrss.xml')
    @observable(events.SitemapsController.latest)
    def latest(self, limit=None, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of all the sites media."""
        if request.settings['rss_display'] != 'True':
            abort(404)

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

        media_query = Media.query.published().order_by(Media.publish_on.desc())
        media = viewable_media(media_query)
        if limit is not None:
            media = media.limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(
            media = media,
            title = 'Latest Media',
        )

    @validate(validators={
        'limit': LimitFeedItemsValidator(),
        'skip': validators.Int(if_empty=0, if_missing=0, if_invalid=0)
    })
    @beaker_cache(expire=60 * 3)
    @expose('sitemaps/mrss.xml')
    @observable(events.SitemapsController.featured)
    def featured(self, limit=None, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of the sites featured media."""
        if request.settings['rss_display'] != 'True':
            abort(404)

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

        media_query = Media.query.in_category(get_featured_category())\
            .published()\
            .order_by(Media.publish_on.desc())
        media = viewable_media(media_query)
        if limit is not None:
            media = media.limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(
            media = media,
            title = 'Featured Media',
        )

    @expose()
    def crossdomain_xml(self, **kwargs):
        """Serve the crossdomain XML file manually if static_files is disabled.

        If someone forgets to add this Alias we might as well serve this file
        for them and save everyone the trouble. This only works when MediaDrop
        is served out of the root of a domain and if Cooliris is enabled.
        """
        global crossdomain_app

        if not request.settings['appearance_enable_cooliris']:
            # Ensure the cache is cleared if cooliris is suddenly disabled
            if crossdomain_app:
                crossdomain_app = None
            raise HTTPNotFound()

        if not crossdomain_app:
            relpath = 'mediadrop/public/crossdomain.xml'
            abspath = os.path.join(config['here'], relpath)
            crossdomain_app = FileApp(abspath)

        return forward(crossdomain_app)
示例#5
0
class CategoriesController(BaseController):
    """
    Categories Controller

    Handles the display of the category hierarchy, displaying the media
    associated with any given category and its descendants.

    """
    def __before__(self, *args, **kwargs):
        """Load all our category data before each request."""
        BaseController.__before__(self, *args, **kwargs)

        c.categories = Category.query\
            .order_by(Category.name)\
            .options(orm.undefer('media_count_published'))\
            .populated_tree()

        counts = dict((cat.id, cat.media_count_published)
                      for cat, depth in c.categories.traverse())
        c.category_counts = counts.copy()
        for cat, depth in c.categories.traverse():
            count = counts[cat.id]
            if count:
                for ancestor in cat.ancestors():
                    c.category_counts[ancestor.id] += count

        category_slug = request.environ['pylons.routes_dict'].get('slug', None)
        if category_slug:
            c.category = fetch_row(Category, slug=category_slug)
            c.breadcrumb = c.category.ancestors()
            c.breadcrumb.append(c.category)

    @expose('categories/index.html')
    @observable(events.CategoriesController.index)
    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,
        )

    @expose('categories/more.html')
    @paginate('media', items_per_page=20)
    @observable(events.CategoriesController.more)
    def more(self, slug, order, page=1, **kwargs):
        media = Media.query.published()\
            .in_category(c.category)

        if order == 'latest':
            media = media.order_by(Media.publish_on.desc())
        else:
            media = media.order_by(Media.popularity_points.desc())

        return dict(
            media=viewable_media(media),
            order=order,
        )

    @validate(validators={'limit': LimitFeedItemsValidator()})
    @beaker_cache(expire=60 * 3, query_args=True)
    @expose('sitemaps/mrss.xml')
    @observable(events.CategoriesController.feed)
    def feed(self, limit=None, **kwargs):
        """ Generate a media rss feed of the latest media

        :param limit: the max number of results to return. Defaults to 30

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

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

        media = Media.query.published()

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

        media_query = media.order_by(Media.publish_on.desc())
        media = viewable_media(media_query)
        if limit is not None:
            media = media.limit(limit)

        return dict(
            media=media,
            title=u'%s Media' % c.category.name,
        )