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({}))
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, )
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)
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, )