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.views += 1 next_episode = None 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.is_published: next_episode = DBSession.query(Media)\ .filter(Media.podcast_id == media.podcast.id)\ .filter(Media.publish_on > media.publish_on)\ .filter(Media.publish_on < datetime.now())\ .filter(Media.status >= 'publish')\ .filter(Media.status.excludes('trash'))\ .order_by(Media.publish_on)\ .first() return dict( media = media, comment_form = post_comment_form, comment_form_action = url_for(action='comment'), comment_form_values = kwargs, next_episode = next_episode, )
def edit(self, id, **kwargs): """Display the :class:`~mediacore.forms.users.UserForm` for editing or adding. :param id: User ID :type id: ``int`` or ``"new"`` :rtype: dict :returns: user The :class:`~mediacore.model.auth.User` instance we're editing. user_form The :class:`~mediacore.forms.users.UserForm` instance. user_action ``str`` form submit url user_values ``dict`` form values """ user = fetch_row(User, id) if tmpl_context.action == "save" or id == "new": # Use the values from error_handler or GET for new users user_values = kwargs user_values["login_details.password"] = None user_values["login_details.confirm_password"] = None else: user_values = dict( display_name=user.display_name, email_address=user.email_address, login_details=dict(group=user.groups[0].group_id if user.groups else None, user_name=user.user_name), ) return dict(user=user, user_form=user_form, user_action=url_for(action="save"), user_values=user_values)
def post_login(self, came_from=url_for(controller="/admin")): if not request.identity: login_counter = request.environ["repoze.who.logins"] + 1 redirect(came_from) userid = request.identity["repoze.who.userid"] flash(_("Welcome back, %s!") % userid) redirect(came_from)
def podcast_image_url(podcast, size='s'): if not podcast: return None image = 'podcasts/%d%s.jpg' % (podcast.id, size) file_name = os.path.join( config.image_dir, image ) if not os.path.isfile(file_name): return None file_url = '/images/' + image return url_for(file_url)
def _jsonify(self, media): im_path = '/images/media/%d%%s.jpg' % media.id if media.podcast_id: media_url = url_for(controller='/media', action='view', slug=media.slug, podcast_slug=media.podcast.slug) else: media_url = url_for(controller="/media", action="view", slug=media.slug) return dict( title = media.title, description = media.description, description_plain = helpers.strip_xhtml(helpers.line_break_xhtml(\ helpers.line_break_xhtml(media.description))), img_l = url_for(im_path % 'l'), img_m = url_for(im_path % 'm'), img_s = url_for(im_path % 's'), img_ss = url_for(im_path % 'ss'), id = media.id, slug = media.slug, url = media_url, podcast = media.podcast and media.podcast.slug or None, )
def upload_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. .. note:: This method returns incorrect Content-Type headers: Content-Type is set to ``text/html`` even though the returned data is really of type ``application/json``. This is because this method is used from the flash based uploader; Swiff.Uploader (which we use) uses Flash's FileReference.upload() method, which doesn't allow overriding the HTTP headers. On windows, the default headers have an "Accept: text/\*" line. This means that it won't accept "application/json". TG honours that, and, when returning, will throw an error rather than respond with an unacceptable Content-Type. It would perhaps be more correct to set Content-Type to ``text/plain`` or ``text/x-json``, but there seems to be a bug in the current TG 2.0.3 + Pylons 0.9.7 stack w.r.t. overriding the Content-Type headers. """ # TODO: look into the bug outlined in the note above. 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] return json.dumps(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 return json.dumps(dict( success = False )) # else actually save it! kwargs.setdefault('name') kwargs.setdefault('tags') media_obj = self._save_media_obj( kwargs['name'], kwargs['email'], kwargs['title'], kwargs['description'], kwargs['tags'], kwargs['file'] ) email.send_media_notification(media_obj) return json.dumps(dict( success = True, redirect = url_for(action='upload_success') ))
from formencode import validators from paste.deploy.converters import asbool from paste.util import mimeparse from mediacore.lib.base import (BaseController, url_for, redirect, expose, expose_xhr, validate, paginate) from mediacore.model import (DBSession, fetch_row, get_available_slug, Media, MediaFile, Comment, Tag, Topic, Author, AuthorWithIP, Podcast) from mediacore.lib import helpers, email from mediacore.forms.media import UploadForm from mediacore.forms.comments import PostCommentForm post_comment_form = PostCommentForm() upload_form = UploadForm( action = url_for(controller='/media', action='upload_submit'), async_action = url_for(controller='/media', action='upload_submit_async') ) class FTPUploadException(Exception): pass class MediaController(BaseController): """Media actions -- for both regular and podcast media""" def __init__(self, *args, **kwargs): """Populate the :obj:`pylons.tmpl_context` with topics. Used by :data:`mediacore.templates.helpers` to render the
def post_logout(self, came_from=url_for("/")): flash(_("We hope to see you soon!")) redirect(came_from)
def login(self, came_from=url_for("/")): login_counter = request.environ["repoze.who.logins"] if login_counter > 0: flash(_("Wrong credentials"), "warning") return dict(login_counter=str(login_counter), came_from=came_from)
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from tg import config, request, response, tmpl_context from sqlalchemy import orm, sql from repoze.what.predicates import has_permission from mediacore.lib.base import (BaseController, url_for, redirect, expose, expose_xhr, validate, paginate) from mediacore.lib import helpers from mediacore.model import DBSession, fetch_row, Setting from mediacore.forms.settings import SettingsForm settings_form = SettingsForm(action=url_for(controller='/settingadmin', action='save')) class SettingadminController(BaseController): allow_only = has_permission('admin') @expose() def index(self, section='topics', **kwargs): redirect(controller='categoryadmin', category=section) @expose('mediacore.templates.admin.settings.edit') def edit(self, **kwargs): """Display the :class:`~mediacore.forms.settings.SettingsForm`. :rtype: dict
def edit_file(self, id, file_id, player_enabled, feed_enabled, toggle_feed, toggle_player, delete, **kwargs): """Save action for the :class:`~mediacore.forms.media.EditFileForm`. Changes or delets a :class:`~mediacore.model.media.MediaFile`. :param id: Media ID :type id: :class:`int` :rtype: JSON dict :returns: success bool message Error message, if unsuccessful status_form Rendered XHTML for the status form, updated to reflect the changes made. """ media = fetch_row(Media, id, incl_trash=True) data = {} # try: try: file = [file for file in media.files if file.id == file_id][0] except IndexError: raise Exception, 'File does not exist.' if toggle_player: data['field'] = 'player_enabled' file.enable_player = data['value'] = not player_enabled DBSession.add(file) elif toggle_feed: data['field'] = 'feed_enabled' file.enable_feed = data['value'] = not feed_enabled # Raises an exception if it is the only feed enabled file for # an already published podcast episode. DBSession.add(file) elif delete: data['field'] = 'delete' DBSession.delete(file) media.files.remove(file) else: raise Exception, 'No action to perform.' data['success'] = True media.update_type() media.update_status() DBSession.add(media) # except Exception, e: # data['success'] = False # data['message'] = e.message 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)) data['status_form'] = status_form_xhtml return data else: DBSession.flush() redirect(action='edit')
def add_file(self, id, file=None, url=None, **kwargs): """Save action for the :class:`~mediacore.forms.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 with :func:`~mediacore.model.media.create_media_stub`. :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.media.EditFileForm` for this file. status_form The rendered XHTML :class:`~mediacore.forms.media.UpdateStatusForm` """ if id == 'new': media = create_media_stub() else: media = fetch_row(Media, id, incl_trash=True) try: if file is not None: # Create a media object, add it to the video, and store the file permanently. media_file = _add_new_media_file(media, file.filename, file.file) elif url: media_file = MediaFile() # Parse the URL checking for known embeddables like YouTube for type, info in config.embeddable_filetypes.iteritems(): match = re.match(info['pattern'], url) if match: media_file.type = type media_file.url = match.group('id') media_file.enable_feed = False break else: # Check for types we can play ourselves type = os.path.splitext(url)[1].lower()[1:] for playable_types in config.playable_types.itervalues(): if type in playable_types: media_file.type = type media_file.url = url break else: raise Exception, 'Unsupported URL %s' % url else: raise Exception, 'Given no action to perform.' media.files.append(media_file) media.update_type() media.update_status() DBSession.add(media) DBSession.flush() # 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)) return dict( success = True, media_id = media.id, file_id = media_file.id, edit_form = edit_form_xhtml, status_form = status_form_xhtml, ) except Exception, e: return dict( success = False, message = e.message, )
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.media.MediaForm` instance media_action ``str`` form submit url media_values ``dict`` form values file_add_form The :class:`~mediacore.forms.media.AddFileForm` instance file_add_action ``str`` form submit url file_edit_form The :class:`~mediacore.forms.media.EditFileForm` instance file_edit_action ``str`` form submit url album_art_form The :class:`~mediacore.forms.admin.AlbumArtForm` instance album_art_action ``str`` form submit url update_status_form The :class:`~mediacore.forms.media.UpdateStatusForm` instance update_status_action ``str`` form submit url """ media = fetch_row(Media, id, incl_trash=True) 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)), topics = [topic.id for topic in media.topics], notes = media.notes, details = dict(duration = helpers.duration_from_seconds(media.duration)), ) # Re-verify the state of our Media object in case the data is nonsensical if id != 'new': media.update_type() media.update_status() DBSession.add(media) return dict( media = media, media_form = media_form, media_action = url_for(action='save'), media_values = media_values, 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'), album_art_form = album_art_form, album_art_action = url_for(action='save_album_art'), update_status_form = update_status_form, update_status_action = url_for(action='update_status'), )
from mediacore.model import (DBSession, fetch_row, get_available_slug, Media, MediaFile, Podcast, Tag, Author) from mediacore.lib import helpers from mediacore.model.media import create_media_stub from mediacore.controllers.media import _add_new_media_file from mediacore.forms.admin import SearchForm, AlbumArtForm from mediacore.forms.media import (MediaForm, AddFileForm, EditFileForm, UpdateStatusForm, PodcastFilterForm) media_form = MediaForm() add_file_form = AddFileForm() edit_file_form = EditFileForm() album_art_form = AlbumArtForm() update_status_form = UpdateStatusForm() search_form = SearchForm(action=url_for(controller='/mediaadmin', action='index')) podcast_filter_form = PodcastFilterForm(action=url_for(controller='/mediaadmin', action='index')) class MediaadminController(BaseController): allow_only = has_permission('admin') @expose_xhr('mediacore.templates.admin.media.index', 'mediacore.templates.admin.media.index-table') @paginate('media', items_per_page=25) 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
Comment Moderation Controller """ from tg import config, request, response, tmpl_context from repoze.what.predicates import has_permission from sqlalchemy import orm, sql from mediacore.lib.base import (BaseController, url_for, redirect, expose, expose_xhr, validate, paginate) from mediacore.model import DBSession, fetch_row, Media, Comment from mediacore.lib import helpers from mediacore.forms.admin import SearchForm from mediacore.forms.comments import EditCommentForm edit_form = EditCommentForm() search_form = SearchForm(action=url_for(controller='/commentadmin', action='index')) class CommentadminController(BaseController): allow_only = has_permission('admin') @expose_xhr('mediacore.templates.admin.comments.index', 'mediacore.templates.admin.comments.index-table') @paginate('comments', items_per_page=50) 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