def index(self, page=1, **kwargs): """List podcasts and podcast media. Our custom paginate decorator allows us to have fewer podcast episodes display on the first page than on the rest with the ``items_first_page`` param. See :class:`mediacore.lib.custompaginate.CustomPage`. :param page: Page number, defaults to 1. :type page: int :rtype: dict :returns: podcasts The :class:`~mediacore.model.podcasts.Podcast` instance episodes The list of :class:`~mediacore.model.media.Media` instances for this page. """ episodes = ( DBSession.query(Media) .filter(Media.podcast_id != None) .order_by(Media.publish_on.desc()) .options(orm.undefer("comment_count_published")) ) episodes = self._filter(episodes) podcasts = DBSession.query(Podcast).options(orm.undefer("published_media_count")).all() return dict(podcasts=podcasts, episodes=episodes)
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, }
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, }
def _update_settings(self, values): """Modify the settings associated with the given dictionary.""" for name, value in values.iteritems(): if name in tmpl_context.settings: setting = tmpl_context.settings[name] else: setting = Setting(key=name, value=value) if value is None: value = u'' else: value = unicode(value) if setting.value != value: setting.value = value DBSession.add(setting) DBSession.flush() # Clear the settings cache unless there are multiple processes. # We have no way of notifying the other processes that they need # to clear their caches too, so we've just gotta let it play out # until all the caches expire. if not request.environ.get('wsgi.multiprocess', False): app_globals.settings_cache.clear() else: # uWSGI provides an automagically included module # that we can use to call a graceful restart of all # the uwsgi processes. # http://projects.unbit.it/uwsgi/wiki/uWSGIReload try: import uwsgi uwsgi.reload() except ImportError: pass
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)
def index(self, page=1, search=None, podcast_filter=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_filter The given podcast ID to filter by, if any podcast_filter_title The podcast name for rendering if a ``podcast_filter`` was specified. podcast_filter_form The :class:`~mediacore.forms.media.PodcastFilterForm` instance. """ media = DBSession.query(Media)\ .filter(Media.status.excludes('trash'))\ .options(orm.undefer('comment_count_published'))\ .options(orm.undefer('comment_count_unreviewed'))\ .order_by(Media.status.desc(), Media.publish_on.desc(), Media.modified_on.desc()) if search is not None: like_search = '%' + search + '%' media = media.filter(sql.or_( Media.title.like(like_search), Media.description.like(like_search), Media.notes.like(like_search), Media.tags.any(Tag.name.like(like_search)), )) podcast_filter_title = podcast_filter if podcast_filter == 'Unfiled': media = media.filter(~Media.podcast.has()) elif podcast_filter is not None and podcast_filter != 'All Media': media = media.filter(Media.podcast.has(Podcast.id == podcast_filter)) podcast_filter_title = DBSession.query(Podcast.title).get(podcast_filter) podcast_filter = int(podcast_filter) return dict( media = media, podcast_filter = podcast_filter, podcast_filter_title = podcast_filter_title, podcast_filter_form = podcast_filter_form, search = search, search_form = search_form, )
def rate(self, slug, rating=1, **kwargs): """Rate up or down the given media. :param slug: The media :attr:`~mediacore.model.media.Media.slug` :param rating: ``1`` or ``0`` if the rating is up or down. :rtype: JSON dict :returns: succcess bool upRating Pluralized count of up raters, "# people" or "1 person" downRating Pluralized count of down raters, "# people" or "1 person" """ media = fetch_row(Media, slug=slug) if rating > 0: media.rating.add_vote(1) else: media.rating.add_vote(0) DBSession.add(media) if request.is_xhr: return dict( success = True, upRating = helpers.text.plural(media.rating.sum, 'person', 'people'), downRating = None, ) else: redirect(action='view')
def save(self, id, delete, category='topics', **kwargs): """Save changes or create a topic or tag. See :class:`~mediacore.forms.categories.EditCategoryForm` for POST vars. :param id: Topic or tag ID :param category: ``topics`` or ``tags`` :param delete: If true the category is deleted rather than saved. :type delete: bool :rtype: JSON dict :returns: success bool category ``topics`` or ``tags`` """ model = self.select_model(category) item = fetch_row(model, id) if delete: DBSession.delete(item) item = None else: item.name = kwargs['name'] item.slug = get_available_slug(model, kwargs['slug'], item) DBSession.add(item) if request.is_xhr: return dict(success=True, category=item) else: redirect(action='index', category=category)
def _add_new_media_file(media, original_filename, file): # FIXME: I think this will raise a KeyError if the uploaded # file doesn't have an extension. file_ext = os.path.splitext(original_filename)[1].lower()[1:] # set the file paths depending on the file type media_file = MediaFile() media_file.type = file_ext media_file.url = 'dummy_url' # model requires that url not NULL media_file.is_original = True media_file.enable_player = media_file.is_playable media_file.enable_feed = not media_file.is_embeddable media_file.size = os.fstat(file.fileno())[6] # update media relations media.files.append(media_file) # add the media file (and its media, if new) to the database to get IDs DBSession.add(media_file) DBSession.flush() # copy the file to its permanent location file_name = '%d_%d_%s.%s' % (media.id, media_file.id, media.slug, file_ext) file_url = _store_media_file(file, file_name) media_file.url = file_url return media_file
def comment(self, slug, **values): """Post a comment from :class:`~mediacore.forms.media.PostCommentForm`. :param slug: The media :attr:`~mediacore.model.media.Media.slug` :returns: Redirect to :meth:`view` page for media. """ if tmpl_context.form_errors: if request.is_xhr: return dict( success = False, errors = tmpl_context.form_errors ) else: redirect(action='view') media = fetch_row(Media, slug=slug) c = Comment() c.status = 'unreviewed' c.author = AuthorWithIP(values['name'], None, request.environ['REMOTE_ADDR']) c.subject = 'Re: %s' % media.title c.body = helpers.clean_xhtml(values['body']) media.comments.append(c) DBSession.add(media) email.send_comment_notification(media, c) if request.is_xhr: return dict(success = True) else: redirect(action='view')
def main(parser, options, args): app_globs = app_globals._current_obj() app_id = app_globals.settings['facebook_appid'] if not app_id: print 'No Facebook app_id configured, exiting' sys.exit(3) app_secret = options.app_secret fb = FacebookAPI(app_id, app_secret) from mediacore.model import DBSession, Media # eager loading of 'meta' to speed up later check. all_media = Media.query.options(joinedload('_meta')).all() print 'Checking all media for existing Facebook comments' progress = ProgressBar(maxval=len(all_media)).start() for i, media in enumerate(all_media): progress.update(i + 1) if 'facebook-comment-xid' not in media.meta: continue if not fb.has_xid_comments(media): continue media.meta[u'facebook-comment-xid'] = unicode(media.id) DBSession.add(media) DBSession.commit() progress.finish()
def main(parser, options, args): app_globs = app_globals._current_obj() app_id = app_globals.settings['facebook_appid'] if not app_id: print 'No Facebook app_id configured, exiting' sys.exit(3) app_secret = options.app_secret fb = FacebookAPI(app_id, app_secret) from mediacore.model import DBSession, Media # eager loading of 'meta' to speed up later check. all_media = Media.query.options(joinedload('_meta')).all() print 'Checking all media for existing Facebook comments' progress = ProgressBar(maxval=len(all_media)).start() for i, media in enumerate(all_media): progress.update(i+1) if 'facebook-comment-xid' not in media.meta: continue if not fb.has_xid_comments(media): continue media.meta[u'facebook-comment-xid'] = unicode(media.id) DBSession.add(media) DBSession.commit() progress.finish()
def add_external_file(self, media, url=u'http://site.example/videos.mp4'): previous_files = len(media.files) media_file = add_new_media_file(media, url=url) # add_new_media_file will set media_file.media AND media.files.append # so we have two files for the media until the session is refreshed. DBSession.refresh(media) assert_length(previous_files+1, media.files) return media_file
def add_external_file(self, media, url=u'http://site.example/videos.mp4'): previous_files = len(media.files) media_file = add_new_media_file(media, url=url) # add_new_media_file will set media_file.media AND media.files.append # so we have two files for the media until the session is refreshed. DBSession.refresh(media) assert_length(previous_files + 1, media.files) return media_file
class MediaForm(ListForm): template = 'admin/box-form.html' id = 'media-form' css_class = 'form' submit_text = None show_children_errors = True _name = 'media-form' # TODO: Figure out why this is required?? fields = [ SingleSelectField('podcast', label_text=N_('Include in the Podcast'), css_classes=['dropdown-select'], help_text=N_('Optional'), options=lambda: [(None, None)] + DBSession.query( Podcast.id, Podcast.title).all()), TextField('slug', label_text=N_('Permalink'), maxlength=50), TextField('title', label_text=N_('Title'), validator=TextField.validator(not_empty=True), maxlength=255), TextField('author_name', label_text=N_('Author Name'), maxlength=50), TextField('author_email', label_text=N_('Author Email'), validator=email_validator(not_empty=True), maxlength=255), XHTMLTextArea('description', label_text=N_('Description'), attrs=dict(rows=5, cols=25)), CategoryCheckBoxList( 'categories', label_text=N_('Categories'), options=lambda: DBSession.query(Category.id, Category.name).all()), TextArea('tags', label_text=N_('Tags'), attrs=dict(rows=3, cols=15), help_text=N_(u'e.g.: puppies, great dane, adorable')), TextArea('notes', label_text=N_('Administrative Notes'), attrs=dict(rows=3, cols=25), container_attrs=lambda: ({ 'class': 'hidden' }, {})[bool( app_globals.settings.get( 'wording_display_administrative_notes', ''))], default=lambda: app_globals.settings[ 'wording_administrative_notes']), SubmitButton('save', default=N_('Save'), named_button=True, css_classes=['btn', 'blue', 'f-rgt']), SubmitButton('delete', default=N_('Delete'), named_button=True, css_classes=['btn', 'f-lft']), ] def post_init(self, *args, **kwargs): events.Admin.MediaForm(self)
def test_can_restrict_query_if_user_does_not_have_the_required_permission(self): query = Media.query permission = u'view' perm = self.perm() view_permission = DBSession.query(Permission).filter(Permission.permission_name == permission).one() view_permission.groups = [] DBSession.flush() assert_none(self.policy.access_condition_for_query(query, permission, perm))
def _create_user_without_groups(self): user = User() user.user_name = u'joe' user.email_address = u'*****@*****.**' user.display_name = u'Joe' user.groups = [] DBSession.add(user) DBSession.flush() return user
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, )
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, )
def save_media_obj(author_name, author_email, title, description, tags, file, url): media = Media() media.author = Author(author_name, author_email) media.title = title media.description = description media.tags = tags add_new_media_file(media, file=file, url=url) DBSession.add(media) DBSession.commit() return media
def _create_user_with_admin_permission_only(self): admin_perm = DBSession.query(Permission).filter(Permission.permission_name == u'admin').one() second_admin_group = Group.example(name=u'Second admin group') admin_perm.groups.append(second_admin_group) admin = User.example(groups=[second_admin_group]) DBSession.commit() perm = MediaCorePermissionSystem.permissions_for_user(admin, config) assert_true(perm.contains_permission(u'admin')) assert_false(perm.contains_permission(u'edit')) return admin
def save_album_art(self, id, album_art, **kwargs): """Save album art uploaded with :class:`~mediacore.forms.media.AlbumArtForm`. :param id: Media ID. If ``"new"`` a new Media stub is created with :func:`~mediacore.model.media.create_media_stub`. :type id: ``int`` or ``"new"`` :param file: The uploaded file :type file: :class:`cgi.FieldStorage` or ``None`` :rtype: JSON dict :returns: success bool message Error message, if unsuccessful id The :attr:`~mediacore.model.media.Media.id` which is important if a new media has just been created. """ if id == 'new': media = create_media_stub() else: media = fetch_row(Media, id, incl_trash=True) im_path = os.path.join(config.image_dir, 'media/%s%s.%s') try: # Create thumbnails im = Image.open(album_art.file) if id == 'new': DBSession.add(media) DBSession.flush() # TODO: Allow other formats? for key, dimensions in config.album_art_sizes.iteritems(): file_path = im_path % (media.id, key, 'jpg') im.resize(dimensions, 1).save(file_path) # Backup the original image just for kicks orig_type = os.path.splitext(album_art.filename)[1].lower()[1:] backup_file = open(im_path % (media.id, 'orig', orig_type), 'w') copyfileobj(album_art.file, backup_file) album_art.file.close() backup_file.close() success = True message = None except IOError: success = False message = 'Unsupported image type' except Exception, e: success = False message = e.message
def _create_user_with_admin_permission_only(self): admin_perm = DBSession.query(Permission).filter( Permission.permission_name == u'admin').one() second_admin_group = Group.example(name=u'Second admin group') admin_perm.groups.append(second_admin_group) admin = User.example(groups=[second_admin_group]) DBSession.commit() perm = MediaCorePermissionSystem.permissions_for_user(admin, config) assert_true(perm.contains_permission(u'admin')) assert_false(perm.contains_permission(u'edit')) return admin
def test_can_restrict_query_if_user_does_not_have_the_required_permission( self): query = Media.query permission = u'view' perm = self.perm() view_permission = DBSession.query(Permission).filter( Permission.permission_name == permission).one() view_permission.groups = [] DBSession.flush() assert_none( self.policy.access_condition_for_query(query, permission, perm))
def index(self, page=1, search=None, media_filter=None, **kwargs): """List comments 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 media_filter: Optional media ID to filter by :type media_filter: int or None :rtype: dict :returns: comments The list of :class:`~mediacore.model.comments.Comment` instances for this page. edit_form The :class:`mediacore.forms.comments.EditCommentForm` instance, to be rendered for each instance in ``comments``. search The given search term, if any search_form The :class:`~mediacore.forms.admin.SearchForm` instance media_filter The given podcast ID to filter by, if any media_filter_title The media title for rendering if a ``media_filter`` was specified. """ comments = DBSession.query(Comment)\ .filter(Comment.status.excludes('trash'))\ .order_by(Comment.status.desc(), Comment.created_on.desc()) if search is not None: like_search = '%' + search + '%' comments = comments.filter(sql.or_( Comment.subject.like(like_search), Comment.body.like(like_search), )) media_filter_title = media_filter if media_filter is not None: comments = comments.filter(Comment.media.has(Media.id == media_filter)) media_filter_title = DBSession.query(Media.title).get(media_filter) media_filter = int(media_filter) return dict( comments = comments, edit_form = edit_form, media_filter = media_filter, media_filter_title = media_filter_title, search = search, search_form = search_form, )
def delete(self, id, **kwargs): """Delete a StorageEngine. :param id: Storage ID. :type id: ``int`` :returns: Redirect back to :meth:`index` after successful delete. """ engine = fetch_row(StorageEngine, id) files = engine.files for f in files: engine.delete(f.unique_id) DBSession.delete(engine) redirect(action='index', id=None)
def delete(self, id, **kwargs): """Delete a user. :param id: User ID. :type id: ``int`` :returns: Redirect back to :meth:`index` after successful delete. """ user = fetch_row(User, id) DBSession.delete(user) if request.is_xhr: return dict(success=True) redirect(action="index", id=None)
def comment(self, slug, **values): """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. """ akismet_key = helpers.fetch_setting('akismet_key') akismet_url = helpers.fetch_setting('akismet_url') if akismet_key: akismet = Akismet(agent='MediaCore/%s' % MEDIACORE_VERSION) akismet.key = akismet_key akismet.blog_url = akismet_url or url_for('/', qualified=True) akismet.verify_key() data = {'comment_author': values['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(values['body'].encode('utf-8'), data): title = "Comment Rejected" text = "Your comment appears to be spam and has been rejected." add_transient_message('comment_posted', title, text) redirect(action='view', anchor='top') media = fetch_row(Media, slug=slug) c = Comment() c.author = AuthorWithIP( values['name'], values['email'], request.environ['REMOTE_ADDR'] ) c.subject = 'Re: %s' % media.title c.body = values['body'] require_review = asbool(helpers.fetch_setting('req_comment_approval')) if not require_review: c.reviewed = True c.publishable = True media.comments.append(c) DBSession.add(media) email.send_comment_notification(media, c) if require_review: title = "Thanks for your comment!" text = "We will post it just as soon as a moderator approves it." add_transient_message('comment_posted', title, text) redirect(action='view', anchor='top') else: redirect(action='view', anchor='comment-%s' % c.id)
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) media.increment_views() DBSession.add(media) 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: related = Media.query.published()\ .options(orm.undefer('comment_count_published'))\ .filter(Media.id != media.id)\ .search('>(%s) <(%s)' % (media.title, media.fulltext.tags))[:6] else: related = [] return dict( media = media, related_media = related, 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, )
def index(self, page=1, **kwargs): """List storage engines with pagination. :rtype: Dict :returns: engines The list of :class:`~mediacore.lib.storage.StorageEngine` instances for this page. """ engines = DBSession.query(StorageEngine)\ .options(orm.undefer('file_count'), orm.undefer('file_size_sum'))\ .all() engines = list(sort_engines(engines)) existing_types = set(ecls.engine_type for ecls in engines) addable_engines = [ ecls for ecls in StorageEngine if not ecls.is_singleton or ecls.engine_type not in existing_types ] return { 'engines': engines, 'addable_engines': addable_engines, }
def index(self, page=1, category='topics', **kwargs): """List topics or tags with pagination. :param category: ``topics`` or ``tags`` :param page: Page number, defaults to 1. :type page: int :rtype: Dict :returns: categories The list of :class:`~mediacore.model.tags.Tag` or :class:`~mediacore.model.topics.Topic` instances for this page. category ``topics`` or ``tags`` category_name ``Topics`` or ``Tags`` edit_form The :class:`~mediacore.forms.categories.EditCategoryForm` instance. """ model = self.select_model(category) categories = DBSession.query(model).order_by(model.name) return dict( categories = categories, category = category, category_name = category.capitalize(), edit_form = edit_form, )
def test_can_fake_logged_in_user(self): admin = DBSession.query(User).filter(User.user_name == u'admin').one() assert_true(admin.has_permission(u'admin')) self.init_fake_request() self.set_authenticated_user(admin) assert_true(has_permission(u'admin'))
def save(self, id, **kwargs): player = fetch_row(PlayerPrefs, id) form = player.settings_form if id == 'new': DBSession.add(player) @validate(form, error_handler=self.edit) def save(id, **kwargs): # Allow the form to modify the player directly # since each can have radically different fields. save_func = getattr(form, 'save_data') save_func(player, **tmpl_context.form_values) redirect(controller='/admin/players', action='index') return save(id, **kwargs)
def feed(self, slug, **kwargs): """Serve the feed as RSS 2.0. If :attr:`~mediacore.model.podcasts.Podcast.feedburner_url` is specified for this podcast, we redirect there. :param slug: A :attr:`~mediacore.model.podcasts.Podcast.slug` :param page: Page number, defaults to 1. :type page: int :rtype: dict :returns: podcast A :class:`~mediacore.model.podcasts.Podcast` instance. episodes A list of :class:`~mediacore.model.media.Media` instances that belong to the ``podcast``. podcasts A list of all the other podcasts """ podcast = fetch_row(Podcast, slug=slug) episodes = self._filter(podcast.media).order_by(Media.publish_on.desc()) podcasts = DBSession.query(Podcast).options(orm.undefer("published_media_count")).all() return dict(podcast=podcast, episodes=episodes, podcasts=podcasts)
def permissions_for_request(cls, environ, config): identity = environ.get('repoze.who.identity', {}) user_id = identity.get('repoze.who.userid') user = None if user_id is not None: user = DBSession.query(User).filter(User.id==user_id).first() return cls.permissions_for_user(user, config)
def _published_media_query(self): """Helper method for getting published media""" return DBSession.query(Media)\ .filter(Media.status >= 'publish')\ .filter(Media.publish_on <= datetime.now())\ .filter(Media.status.excludes('trash'))\ .order_by(Media.publish_on.desc())
class GroupForm(ListForm): template = 'admin/box-form.html' id = 'group-form' css_class = 'form' submit_text = None show_children_errors = True event = events.Admin.GroupForm fields = [ TextField('display_name', label_text=N_('Display Name'), validator=TextField.validator(not_empty=True), maxlength=255), TextField('group_name', label_text=N_('Groupname'), validator=All(PlainText(not_empty=True), UniqueGroupname()), maxlength=16), CheckBoxList( 'permissions', label_text=N_('Group Permissions'), css_classes=['details_fieldset'], options=lambda: DBSession.query(Permission.permission_id, Permission.description).all()), SubmitButton('save', default=N_('Save'), named_button=True, css_classes=['btn', 'btn-save', 'blue', 'f-rgt']), SubmitButton('delete', default=N_('Delete'), named_button=True, css_classes=['btn', 'btn-delete']), ]
def permissions_for_request(cls, environ, config): identity = environ.get('repoze.who.identity', {}) user_id = identity.get('repoze.who.userid') user = None if user_id is not None: user = DBSession.query(User).filter(User.user_id==user_id).first() return cls.permissions_for_user(user, config)
def test_can_fake_logged_in_user(self): admin = DBSession.query(User).filter(User.user_name==u'admin').one() assert_true(admin.has_permission(u'admin')) self.init_fake_request() self.set_authenticated_user(admin) assert_true(has_permission(u'admin'))
def delete(self, id, **kwargs): """Delete a PlayerPref. After deleting the PlayerPref, cleans up the players table, ensuring that each Player class is represented--if the deleted PlayerPref is the last example of that Player class, creates a new disabled PlayerPref for that Player class with the default settings. :param id: Player ID. :type id: ``int`` :returns: Redirect back to :meth:`index` after successful delete. """ player = fetch_row(PlayerPrefs, id) DBSession.delete(player) DBSession.flush() cleanup_players_table() redirect(action='index', id=None)
def rate(self, slug, up=None, down=None, **kwargs): """Say 'I like this' for the given media. :param slug: The media :attr:`~mediacore.model.media.Media.slug` :rtype: unicode :returns: The new number of likes """ # we have to check if current user is anonymous or authenticated userid = check_user_authentication(request) if not userid: log.warn('Anonymous user cannot rate media') raise HTTPUnauthorized().exception media = fetch_row(Media, slug=slug) # check if current user has already voted this media object votes = Vote.query.get_votes(media_id=media.id, user_name=userid) if votes.count(): # if true redirect to 'view' log.warn('User %s already voted this media') redirect(action='view') # create new vote object mapping current media and user vote = Vote() vote.media_id = media.id vote.user_name = userid # Give the Vote object an ID. DBSession.add(vote) DBSession.flush() if up: vote.increment_likes() media.increment_likes() elif down: vote.increment_dislikes() media.increment_dislikes() if request.is_xhr: return u'' else: redirect(action='view')
def save(self, id, engine_type=None, **kwargs): if id == 'new': assert engine_type is not None, 'engine_type must be specified when saving a new StorageEngine.' engine = self.fetch_engine(id, engine_type) form = engine.settings_form if id == 'new': DBSession.add(engine) @validate(form, error_handler=self.edit) def save_engine_params(id, general, **kwargs): # Allow the form to modify the StorageEngine directly # since each can have radically different fields. save_func = getattr(form, 'save_engine_params') save_func(engine, **tmpl_context.form_values) redirect(controller='/admin/storage', action='index') return save_engine_params(id, **kwargs)
def inject_in_db(cls, enable_player=False): from mediacore.model import DBSession from mediacore.model.players import players as players_table, PlayerPrefs prefs = PlayerPrefs() prefs.name = cls.name prefs.enabled = enable_player # didn't get direct SQL expression to work with SQLAlchemy # player_table = sql.func.max(player_table.c.priority) query = sql.select([sql.func.max(players_table.c.priority)]) max_priority = DBSession.execute(query).first()[0] if max_priority is None: max_priority = -1 prefs.priority = max_priority + 1 prefs.created_on = datetime.now() prefs.modified_on = datetime.now() prefs.data = cls.default_data DBSession.add(prefs) DBSession.commit()
def _to_python(self, value, state): id = request.environ['pylons.routes_dict']['id'] query = DBSession.query(User).filter_by(user_name=value) if id != 'new': query = query.filter(User.user_id != id) if query.count() != 0: raise Invalid(_('User name already exists'), value, state) return value
def update_enabled_players(): """Ensure that the encoding status of all media is up to date with the new set of enabled players. The encoding status of Media objects is dependent on there being an enabled player that supports that format. Call this method after changing the set of enabled players, to ensure encoding statuses are up to date. """ from mediacore.model import DBSession, Media media = DBSession.query(Media) for m in media: m.update_status()
def save_media_obj(self, name, email, title, description, tags, uploaded_file, url): # create our media object as a status-less placeholder initially media_obj = Media() media_obj.author = Author(name, email) media_obj.title = title media_obj.slug = get_available_slug(Media, title) media_obj.description = description if request.settings['wording_display_administrative_notes']: media_obj.notes = request.settings['wording_administrative_notes'] media_obj.set_tags(tags) # Give the Media object an ID. DBSession.add(media_obj) DBSession.flush() # Create a MediaFile object, add it to the media_obj, and store the file permanently. media_file = add_new_media_file(media_obj, file=uploaded_file, url=url) # The thumbs may have been created already by add_new_media_file if not has_thumbs(media_obj): create_default_thumbs_for(media_obj) media_obj.update_status() DBSession.flush() return media_obj
def test_audiodesc_video_url_media(self): """Media with both Audio files and Video files attatched should be Video type.""" try: # Create the media object media = self._new_publishable_media( u'description-video', u'(Audio Description + Video)') DBSession.add(media) # Add an audio description media_file = add_new_media_file( media, None, u'http://fakesite.com/fakefile.mp3') media_file.type = AUDIO_DESC media.update_status() # Add a video file media_file = add_new_media_file( media, None, u'http://fakesite.com/fakefile.m4v') media.update_status() # Commit + test DBSession.commit() assert media.type == VIDEO, \ "A Media object with a .m4v file and an Audio Description " \ "was not labelled as a video type; it was labelled %s" % \ (t, media.type) except SQLAlchemyError, e: DBSession.rollback() raise e
def random(self, **kwargs): """Redirect to a randomly selected media item.""" # TODO: Implement something more efficient than ORDER BY RAND(). # This method does a full table scan every time. random_query = Media.query.published().order_by(sql.func.random()) media = viewable_media(random_query).first() if media is None: redirect(action='explore') if media.podcast_id: podcast_slug = DBSession.query(Podcast.slug).get(media.podcast_id) else: podcast_slug = None redirect(action='view', slug=media.slug, podcast_slug=podcast_slug)
def test_add_file_url(self): slug = u'test-add-file-url' title = u'Test Adding File by URL on Media Edit Page.' try: media = self._new_publishable_media(slug, title) media.publishable = False media.reviewed = False DBSession.add(media) DBSession.commit() media_id = media.id except SQLAlchemyError, e: DBSession.rollback() raise e
class ImportVideosForm(ListForm): template = 'admin/box-form.html' id = 'settings-form' css_class = 'form' submit_text = None fields = [ ListFieldSet( 'youtube', suppress_label=True, legend='', css_classes=['details_fieldset'], children=[ TextArea( 'channel_names', attrs=dict(rows=3, cols=20), label_text=_('Channel Name(s)'), help_text= _('One or more channel names (separated by commas) to import. Please enter only the channel/user name, not the full URL. Please be aware that it may take several minutes for the import to complete. When all videos have been imported, you will be returned to the Media page to manage your new videos.' ), validator=NotEmpty), CheckBox( 'auto_publish', label_text=_('Publish Videos'), help_text= _('When this is selected, videos are published automatically when they are imported. Otherwise the videos will be added, but will be waiting for review before being published.' )), CategoryCheckBoxList('categories', label_text=_('Categories'), options=lambda: DBSession.query( Category.id, Category.name).all()), TextArea('tags', label_text=_('Tags'), attrs=dict(rows=3, cols=15), help_text=_(u'e.g.: puppies, great dane, adorable')), SubmitButton('save', default=_('Import'), css_classes=['btn', 'btn-save', 'blue', 'f-rgt']), ]) ]
def map_migrate_version(self): migrate_version_query = migrate_table.select( migrate_table.c.repository_id == u'MediaCore Migrations') result = DBSession.execute(migrate_version_query).fetchone() db_migrate_version = result.version if db_migrate_version in migrate_to_alembic_mapping: return migrate_to_alembic_mapping[db_migrate_version] earliest_upgradable_version = sorted(migrate_to_alembic_mapping)[0] if db_migrate_version < earliest_upgradable_version: error_msg = ( 'Upgrading from such an old version of MediaCore is not ' 'supported. Your database is at version %d but upgrades are only ' 'supported from MediaCore CE 0.9.0 (DB version %d). Please upgrade ' '0.9.0 first.') self.log.error(error_msg % (db_migrate_version, earliest_upgradable_version)) else: self.log.error( 'Unknown DB version %s. Can not upgrade to alembic' % db_migrate_version) raise AssertionError('unsupported DB migration version.')
def test_captioned_url_media(self): """Media with only subtitles attatched should be None type.""" try: for t in self.caption_types: media = self._new_publishable_media( u'caption-%s' % t, u'%s (Captioned)' % t.upper()) DBSession.add(media) media_file = add_new_media_file( media, None, u'http://fakesite.com/fakefile.%s' % t) media.update_status() DBSession.commit() assert media.type == None, \ "A Media object with only an .%s file associated was " \ "not labelled as a 'None' type; it was labelled %s" % \ (t, media.type) except SQLAlchemyError, e: DBSession.rollback() raise e
def _get_media(self, unique): """Return the media/mediafiles required for the Helpers tests""" try: media = self._new_publishable_media(u'media-selection-%s' % unique, u'Media Selection Test (%s)' % unique) DBSession.add(media) media_files = {} for t in ['oga', 'ogv', 'm4a', 'm4v', 'flv', 'mp3', 'xml']: media_files[t] = add_new_media_file(media, None, u'http://fakesite.com/fakefile.%s' % t) media_files['youtube'] = add_new_media_file(media, None, u'http://www.youtube.com/watch?v=3RsbmjNLQkc') media.update_status() DBSession.commit() except SQLAlchemyError, e: DBSession.rollback() raise e
def test_audio_description_url_media(self): """Media with only Audio Descriptions attatched should be None type.""" try: for t in self.audio_types: media = self._new_publishable_media(u'description-%s' % t, u'%s (Audio Description)' % t.upper()) DBSession.add(media) media_file = add_new_media_file(media, None, u'http://fakesite.com/fakefile.%s' % t) media_file.type = AUDIO_DESC media.update_status() DBSession.commit() assert media.type == None, \ "A Media object with only an Audio Description file " \ "associated was not labelled as a None type; it " \ "was labelled %s" % (t, media.type) except SQLAlchemyError, e: DBSession.rollback() raise e