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_media(media_obj, 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)
def _parse(self, url, **kwargs): """Return metadata for the given URL that matches :attr:`url_pattern`. :type url: unicode :param url: A remote URL string. :param \*\*kwargs: The named matches from the url match object. :rtype: dict :returns: Any extracted metadata. """ id = kwargs['id'] yt_service = gdata.youtube.service.YouTubeService() yt_service.ssl = False try: entry = yt_service.GetYouTubeVideoEntry(video_id=id) except gdata.service.RequestError, e: if e['status'] == 403 and e['body'] == 'Private video': raise UserStorageError( _('This video is private and cannot be embedded.')) elif e['status'] == 400 and e['body'] == 'Invalid id': raise UserStorageError( _('Invalid YouTube URL. This video does not exist.'))
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 boolean_radiobuttonlist(name, **kwargs): return RadioButtonList( name, options=lambda: (('true', _('Yes')), ('false', _('No'))), validator=OneOf(['true', 'false']), **kwargs )
class HTML5OrFlashPrefsForm(PlayerPrefsForm): fields = [ RadioButtonList( 'prefer_flash', options=lambda: ( (False, _('Yes, use the Flash Player when the device supports it.')), (True, _('No, use the HTML5 Player when the device supports it.')), ), css_classes=['options'], label_text=N_('Prefer the Flash Player when possible'), validator=StringBool, ), ] + PlayerPrefsForm.buttons event = events.Admin.Players.HTML5OrFlashPrefsForm def display(self, value, player, **kwargs): value.setdefault('prefer_flash', player.data.get('prefer_flash', False)) return PlayerPrefsForm.display(self, value, player, **kwargs) def save_data(self, player, prefer_flash, **kwargs): player.data['prefer_flash'] = prefer_flash
def boolean_radiobuttonlist(name, **kwargs): return RadioButtonList( name, options=lambda: ((True, _('Yes')), (False, _('No'))), validator=StringBool, **kwargs )
def real_boolean_radiobuttonlist(name, **kwargs): # TODO: replace uses of boolean_radiobuttonlist with this, then scrap the old one. return RadioButtonList(name, options=lambda: ((True, _('Yes')), (False, _('No'))), validator=StringBool, **kwargs)
def _parse(self, url, **kwargs): """Return metadata for the given URL that matches :attr:`url_pattern`. :type url: unicode :param url: A remote URL string. :param \*\*kwargs: The named matches from the url match object. :rtype: dict :returns: Any extracted metadata. """ id = kwargs['id'] yt_service = gdata.youtube.service.YouTubeService() yt_service.ssl = False try: entry = yt_service.GetYouTubeVideoEntry(video_id=id) except gdata.service.RequestError, request_error: e = request_error.args[0] if e['status'] == 403 and e['body'] == 'Private video': raise UserStorageError( _('This video is private and cannot be embedded.')) elif e['status'] == 400 and e['body'] == 'Invalid id': raise UserStorageError( _('Invalid YouTube URL. This video does not exist.')) raise UserStorageError(_('YouTube Error: %s') % e['body'])
def real_boolean_radiobuttonlist(name, **kwargs): # TODO: replace uses of boolean_radiobuttonlist with this, then scrap the old one. return RadioButtonList( name, options=lambda: ((True, _('Yes')), (False, _('No'))), validator=StringBool, **kwargs )
def register_default_types(): default_types = [ (VIDEO, _('Video')), (AUDIO, _('Audio')), (AUDIO_DESC, _('Audio Description')), (CAPTIONS, _('Captions')), ] for t in default_types: yield t
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, )
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, )
class AddFileForm(ListForm): template = 'admin/media/file-add-form.html' id = 'add-file-form' submit_text = None fields = [ FileField( 'file', label_text=N_( 'Select an encoded video or audio file on your computer'), validator=FieldStorageUploadConverter(not_empty=False, label_text=N_('Upload'))), SubmitButton('add_url', default=N_('Add URL'), named_button=True, css_class='btn grey btn-add-url f-rgt'), TextField( 'url', validator=URL, suppress_label=True, attrs=lambda: { 'title': _('YouTube, Vimeo, Google Video, Amazon S3 or any other link') }, maxlength=255), ] def post_init(self, *args, **kwargs): events.Admin.AddFileForm(self)
class WXHValidator(FancyValidator): """ width by height validator. example input 1: "800x600" example output 2: (800, 600) example input 2: "" example output 2: (None, None) example input 3: "0x0" example output 3:" (None, None) """ def _to_python(self, value, state=None): if not value.strip(): return (None, None) try: width, height = value.split('x') except ValueError, e: raise Invalid(_('Value must be in the format wxh; e.g. 200x300'), value, state) errors = [] try: width = int(width) except ValueError, e: errors.append(_('Width must be a valid integer'))
def _parse(self, url, **kwargs): """Return metadata for the given URL that matches :attr:`url_pattern`. :type url: unicode :param url: A remote URL string. :param \*\*kwargs: The named matches from the url match object. :rtype: dict :returns: Any extracted metadata. """ id = kwargs['id'] # Ensure the video uses the .com TLD for the API request. url = 'http://www.dailymotion.com/video/%s' % id data_url = 'http://www.dailymotion.com/services/oembed?' + \ urlencode({'format': 'json', 'url': url}) headers = {'User-Agent': USER_AGENT} req = Request(data_url, headers=headers) try: temp_data = urlopen(req) try: data_string = temp_data.read() if data_string == 'This video cannot be embeded.': raise UserStorageError( _('This DailyMotion video does not allow embedding.')) data = simplejson.loads(data_string) finally: temp_data.close() except URLError, e: log.exception(e) data = {}
def _parse(self, url, id, **kwargs): """Return metadata for the given URL that matches :attr:`url_pattern`. :type url: unicode :param url: A remote URL string. :param \*\*kwargs: The named matches from the url match object. :rtype: dict :returns: Any extracted metadata. """ if '?' in url: url += '&skin=api' else: url += '?skin=api' req = Request(url) try: temp_data = urlopen(req) xmlstring = temp_data.read() try: try: xmltree = ElementTree.fromstring(xmlstring) except: temp_data.close() raise except SyntaxError: raise UserStorageError( _('Invalid BlipTV URL. This video does not exist.')) except URLError, e: log.exception(e) raise
def best_translation(a, b): """Return the best translation given a preferred and a fallback string. If we have a translation for our preferred string 'a' or if we are using English, return 'a'. Otherwise, return a translation for the fallback string 'b'. :param a: The preferred string to translate. :param b: The fallback string to translate. :returns: The best translation :rtype: string """ translated_a = _(a) if a != translated_a or translator.locale.language == 'en': return translated_a else: return _(b)
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, )
def send_support_request(email, url, description, get_vars, post_vars): """ Helper method to send a Support Request email in response to a server error. Sends to the address configured in the 'email_support_requests' setting, if it is configured. :param email: The requesting user's email address. :type email: unicode :param url: The url that the user requested assistance with. :type url: unicode :param description: The user's description of their problem. :type description: unicode :param get_vars: The GET variables sent with the failed request. :type get_vars: dict of str -> str :param post_vars: The POST variables sent with the failed request. :type post_vars: dict of str -> str """ send_to = request.settings['email_support_requests'] if not send_to: return get_vars = "\n\n ".join(x + " : " + get_vars[x] for x in get_vars) post_vars = "\n\n ".join([x + " : " + post_vars[x] for x in post_vars]) subject = _('New Support Request: %(email)s') % locals() body = _("""A user has asked for support Email: %(email)s URL: %(url)s Description: %(description)s GET_VARS: %(get_vars)s POST_VARS: %(post_vars)s """) % locals() send(send_to, request.settings['email_send_from'], subject, body)
def save_thumb(self, id, thumb, **kwargs): """Save a thumbnail uploaded with :class:`~mediacore.forms.admin.ThumbForm`. :param id: Media ID. If ``"new"`` a new Media stub is created. :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 = Media() user = request.environ['repoze.who.identity']['user'] media.author = Author(user.display_name, user.email_address) media.title = os.path.basename(thumb.filename) media.slug = get_available_slug(Media, '_stub_' + media.title) DBSession.add(media) DBSession.flush() else: media = fetch_row(Media, id) try: # Create JPEG thumbs create_thumbs_for(media, thumb.file, thumb.filename) success = True message = None except IOError, e: success = False if id == 'new': DBSession.delete(media) if e.errno == 13: message = _('Permission denied, cannot write file') elif e.message == 'cannot identify image file': message = _('Unsupport image type: %s') \ % os.path.splitext(thumb.filename)[1].lstrip('.') elif e.message == 'cannot read interlaced PNG files': message = _('Interlaced PNGs are not supported.') else: raise
def save_thumb(self, id, thumb, **kwargs): """Save a thumbnail uploaded with :class:`~mediacore.forms.admin.ThumbForm`. :param id: Media ID. If ``"new"`` a new Media stub is created. :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 = Media() user = req.perm.user media.author = Author(user.display_name, user.email_address) media.title = os.path.basename(thumb.filename) media.slug = get_available_slug(Media, '_stub_' + media.title) DBSession.add(media) DBSession.flush() else: media = fetch_row(Media, id) try: # Create JPEG thumbs create_thumbs_for(media, thumb.file, thumb.filename) success = True message = None except IOError, e: success = False if id == 'new': DBSession.delete(media) if e.errno == 13: message = _('Permission denied, cannot write file') elif e.message == 'cannot identify image file': message = _('Unsupported image type: %s') \ % os.path.splitext(thumb.filename)[1].lstrip('.') elif e.message == 'cannot read interlaced PNG files': message = _('Interlaced PNGs are not supported.') else: raise
def _to_python(self, value, state=None): if not value.strip(): return (None, None) try: width, height = value.split("x") except ValueError, e: raise Invalid(_("Value must be in the format wxh; e.g. 200x300"), value, state)
def _to_python(self, value, state=None): try: return helpers.duration_to_seconds(value) except ValueError: msg = _('Bad duration formatting, use Hour:Min:Sec') # Colons have special meaning in error messages msg.replace(':', ':') raise formencode.Invalid(msg, value, state)
def _to_python(self, value, state=None): try: return helpers.duration_to_seconds(value) except ValueError: msg = _('Bad duration formatting, use Hour:Min:Sec') # Colons have special meaning in error messages msg.replace(':', ':') raise Invalid(msg, value, state)
def parse(self, file=None, url=None): """Return metadata for the given file or raise an error. :type file: :class:`cgi.FieldStorage` or None :param file: A freshly uploaded file object. :type url: unicode or None :param url: A remote URL string. :rtype: dict :returns: Any extracted metadata. :raises UnsuitableEngineError: If file information cannot be parsed. """ if url is None: raise UnsuitableEngineError if url.startswith('rtmp://'): known_server_uris = self._data.setdefault(RTMP_SERVER_URIS, ()) if RTMP_URI_DIVIDER in url: # Allow the user to explicitly mark the server/file separation parts = url.split(RTMP_URI_DIVIDER) server_uri = parts[0].rstrip('/') file_uri = ''.join(parts[1:]).lstrip('/') if server_uri not in known_server_uris: known_server_uris.append(server_uri) else: # Get the rtmp server from our list of known servers or fail for server_uri in known_server_uris: if url.startswith(server_uri): file_uri = url[len(server_uri.rstrip('/') + '/'):] break else: raise UserStorageError( _('This RTMP server has not been configured. Add it ' 'by going to Settings > Storage Engines > ' 'Remote URLs.')) unique_id = ''.join((server_uri, RTMP_URI_DIVIDER, file_uri)) else: unique_id = url filename = os.path.basename(url) name, ext = os.path.splitext(filename) ext = unicode(ext).lstrip('.').lower() container = guess_container_format(ext) # FIXME: Replace guess_container_format with something that takes # into consideration the supported formats of all the custom # players that may be installed. # if not container or container == 'unknown': # raise UnsuitableEngineError return { 'type': guess_media_type(ext), 'container': container, 'display_name': u'%s.%s' % (name, container or ext), 'unique_id': unique_id, }
def parse(self, file=None, url=None): """Return metadata for the given file or raise an error. :type file: :class:`cgi.FieldStorage` or None :param file: A freshly uploaded file object. :type url: unicode or None :param url: A remote URL string. :rtype: dict :returns: Any extracted metadata. :raises UnsuitableEngineError: If file information cannot be parsed. """ if url is None: raise UnsuitableEngineError if url.startswith('rtmp://'): known_server_uris = self._data.setdefault(RTMP_SERVER_URIS, ()) if RTMP_URI_DIVIDER in url: # Allow the user to explicitly mark the server/file separation parts = url.split(RTMP_URI_DIVIDER) server_uri = parts[0].rstrip('/') file_uri = ''.join(parts[1:]).lstrip('/') if server_uri not in known_server_uris: known_server_uris.append(server_uri) else: # Get the rtmp server from our list of known servers or fail for server_uri in known_server_uris: if url.startswith(server_uri): file_uri = url[len(server_uri.rstrip('/') + '/'):] break else: raise UserStorageError( _('This RTMP server has not been configured. Add it ' 'by going to Settings > Storage Engines > ' 'Remote URLs.')) unique_id = ''.join((server_uri, RTMP_URI_DIVIDER, file_uri)) else: unique_id = url filename = os.path.basename(url) name, ext = os.path.splitext(filename) ext = ext.lstrip('.').lower() container = guess_container_format(ext) # FIXME: Replace guess_container_format with something that takes # into consideration the supported formats of all the custom # players that may be installed. # if not container or container == 'unknown': # raise UnsuitableEngineError return { 'type': guess_media_type(ext), 'container': container, 'display_name': u'%s.%s' % (name, container or ext), 'unique_id': unique_id, }
def _to_python(self, value, state=None): if not value.strip(): return (None, None) try: width, height = value.split('x') except ValueError, e: raise Invalid(_('Value must be in the format wxh; e.g. 200x300'), value, state)
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(), )
def save_thumb(self, id, thumb, **values): """Save a thumbnail uploaded with :class:`~mediacore.forms.admin.ThumbForm`. :param id: Media ID. If ``"new"`` a new Podcast stub is created. :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.podcasts.Podcast.id` which is important if a new podcast has just been created. """ if id == 'new': return dict( success=False, message= u'You must first save the podcast before you can upload a thumbnail', ) podcast = fetch_row(Podcast, id) try: # Create JPEG thumbs create_thumbs_for(podcast, thumb.file, thumb.filename) success = True message = None except IOError, e: success = False if e.errno == 13: message = _('Permission denied, cannot write file') elif e.message == 'cannot identify image file': message = _('Unsupport image type: %s') \ % os.path.splitext(thumb.filename)[1].lstrip('.') elif e.message == 'cannot read interlaced PNG files': message = _('Interlaced PNGs are not supported.') else: raise
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 _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 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'))
def save_thumb(self, id, thumb, **values): """Save a thumbnail uploaded with :class:`~mediacore.forms.admin.ThumbForm`. :param id: Media ID. If ``"new"`` a new Podcast stub is created. :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.podcasts.Podcast.id` which is important if a new podcast has just been created. """ if id == 'new': return dict( success = False, message = u'You must first save the podcast before you can upload a thumbnail', ) podcast = fetch_row(Podcast, id) try: # Create JPEG thumbs create_thumbs_for(podcast, thumb.file, thumb.filename) success = True message = None except IOError, e: success = False if e.errno == 13: message = _('Permission denied, cannot write file') elif e.message == 'cannot identify image file': message = _('Unsupport image type: %s') \ % os.path.splitext(thumb.filename)[1].lstrip('.') elif e.message == 'cannot read interlaced PNG files': message = _('Interlaced PNGs are not supported.') else: raise
def doc_link(page=None, anchor="", text=N_("Help"), **kwargs): """Return a link (anchor element) to the documentation on the project site. XXX: Target attribute is not XHTML compliant. """ attrs = {"href": "http://mediacorecommunity.org/docs/user/%s.html#%s" % (page, anchor), "target": "_blank"} if kwargs: attrs.update(kwargs) attrs_string = " ".join(['%s="%s"' % (key, attrs[key]) for key in attrs]) out = "<a %s>%s</a>" % (attrs_string, _(text)) return literal(out)
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)
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(), )
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)
def doc_link(page=None, anchor='', text=N_('Help'), **kwargs): """Return a link (anchor element) to the documentation on the project site. XXX: Target attribute is not XHTML compliant. """ attrs = { 'href': 'http://mediadrop.net/docs/user/%s.html#%s' % (page, anchor), 'target': '_blank', } if kwargs: attrs.update(kwargs) attrs_string = ' '.join(['%s="%s"' % (key, attrs[key]) for key in attrs]) out = '<a %s>%s</a>' % (attrs_string, _(text)) return literal(out)
def doc_link(page=None, anchor='', text=N_('Help'), **kwargs): """Return a link (anchor element) to the documentation on the project site. XXX: Target attribute is not XHTML compliant. """ attrs = { 'href': 'http://getmediacore.com/docs/user/%s.html#%s' % (page, anchor), 'target': '_blank', } if kwargs: attrs.update(kwargs) attrs_string = ' '.join(['%s="%s"' % (key, attrs[key]) for key in attrs]) out = '<a %s>%s</a>' % (attrs_string, _(text)) return literal(out)
def display_name(self): """Return the user-friendly display name for this player class. This string is expected to be i18n-ready. Simply wrap it in a call to :func:`mediacore.lib.i18n._`. :rtype: unicode :returns: A i18n-ready string name. """ if self.player_cls is None: # do not break the admin interface (admin/settings/players) if the # player is still in the database but the actual player class is not # available anymore (this can happen especially for players provided # by external plugins. return _(u'%s (broken)') % self.name return self.player_cls.display_name
def _verify_upload_integrity(self, file, file_url): """Download the given file from the URL and compare the SHA1s. :type file: :class:`cgi.FieldStorage` :param file: A freshly uploaded file object, that has just been sent to the FTP server. :type file_url: str :param file_url: A publicly accessible URL where the uploaded file can be downloaded. :returns: `True` if the integrity check succeeds or is disabled. :raises FTPUploadError: If the file cannot be downloaded after the max number of retries, or if the the downloaded file doesn't match the original. """ max_tries = int(self._data[FTP_MAX_INTEGRITY_RETRIES]) if max_tries < 1: return True file.seek(0) orig_hash = sha1(file.read()).hexdigest() # Try to download the file. Increase the number of retries, or the # timeout duration, if the server is particularly slow. # eg: Akamai usually takes 3-15 seconds to make an uploaded file # available over HTTP. for i in xrange(max_tries): try: temp_file = urlopen(file_url) dl_hash = sha1(temp_file.read()).hexdigest() temp_file.close() except HTTPError, http_err: # Don't raise the exception now, wait until all attempts fail time.sleep(3) else: # If the downloaded file matches, success! Otherwise, we can # be pretty sure that it got corrupted during FTP transfer. if orig_hash == dl_hash: return True else: msg = _('The file transferred to your FTP server is '\ 'corrupted. Please try again.') raise FTPUploadError(msg, None, None)
def display(self, value=None, **kwargs): if value: # XXX: First, try to translate this string. This is necessary for # the "Legal Wording" section of the Upload settings. The # default string was extracted correctly but not translated. value = _(value) value = line_break_xhtml(value) # Enable the rich text editor, if dictated by the settings: if tiny_mce_condition(): if 'css_classes' in kwargs: kwargs['css_classes'].append('tinymcearea') else: kwargs['css_classes'] = ['tinymcearea'] return TextArea.display(self, value, **kwargs)
def store(self, media_file, file=None, url=None, meta=None): """Store the given file or URL and return a unique identifier for it. :type media_file: :class:`~mediacore.model.media.MediaFile` :param media_file: The associated media file object. :type file: :class:`cgi.FieldStorage` or None :param file: A freshly uploaded file object. :type url: unicode or None :param url: A remote URL string. :type meta: dict :param meta: The metadata returned by :meth:`parse`. :rtype: unicode or None :returns: The unique ID string. Return None if not generating it here. :raises FTPUploadError: If storing the file fails. """ file_name = safe_file_name(media_file, file.filename) file_url = os.path.join(self._data[HTTP_DOWNLOAD_URI], file_name) upload_dir = self._data[FTP_UPLOAD_DIR] stor_cmd = 'STOR ' + file_name ftp = self._connect() try: if upload_dir: ftp.cwd(upload_dir) ftp.storbinary(stor_cmd, file.file) # Raise a FTPUploadError if the file integrity check fails # TODO: Delete the file if the integrity check fails self._verify_upload_integrity(file.file, file_url) ftp.quit() except ftp_errors, e: log.exception(e) ftp.quit() msg = _('Could not upload the file from your FTP server: %s')\ % e.message raise FTPUploadError(msg, None, None)
def document(self, *args, **kwargs): """Render the error document for the general public. Essentially, when an error occurs, a second request is initiated for the URL ``/error/document``. The URL is set on initialization of the :class:`pylons.middleware.StatusCodeRedirect` object, and can be overridden in :func:`tg.configuration.add_error_middleware`. Also, before this method is called, some potentially useful environ vars are set in :meth:`pylons.middleware.StatusCodeRedirect.__call__` (access them via :attr:`tg.request.environ`). :rtype: Dict :returns: prefix The environ SCRIPT_NAME. vars A dict containing the first 2 KB of the original request. code Integer error code thrown by the original request, but it can also be overriden by setting ``tg.request.params['code']``. message A message to display to the user. Pulled from ``tg.request.params['message']``. """ request = self._py_object.request environ = request.environ original_request = environ.get('pylons.original_request', None) original_response = environ.get('pylons.original_response', None) default_message = '<p>%s</p>' % _("We're sorry but we weren't able " "to process this request.") message = request.params.get('message', default_message) message = clean_xhtml(message) return dict( prefix=environ.get('SCRIPT_NAME', ''), code=int( request.params.get( 'code', getattr(original_response, 'status_int', 500))), message=message, vars=dict(POST_request=unicode(original_request)[:2048]), )
def document(self, *args, **kwargs): """Render the error document for the general public. Essentially, when an error occurs, a second request is initiated for the URL ``/error/document``. The URL is set on initialization of the :class:`pylons.middleware.StatusCodeRedirect` object, and can be overridden in :func:`tg.configuration.add_error_middleware`. Also, before this method is called, some potentially useful environ vars are set in :meth:`pylons.middleware.StatusCodeRedirect.__call__` (access them via :attr:`tg.request.environ`). :rtype: Dict :returns: prefix The environ SCRIPT_NAME. vars A dict containing the first 2 KB of the original request. code Integer error code thrown by the original request, but it can also be overriden by setting ``tg.request.params['code']``. message A message to display to the user. Pulled from ``tg.request.params['message']``. """ request = self._py_object.request environ = request.environ original_request = environ.get('pylons.original_request', None) original_response = environ.get('pylons.original_response', None) default_message = '<p>%s</p>' % _("We're sorry but we weren't able " "to process this request.") message = request.params.get('message', default_message) message = clean_xhtml(message) return dict( prefix = environ.get('SCRIPT_NAME', ''), code = int(request.params.get('code', getattr(original_response, 'status_int', 500))), message = message, vars = dict(POST_request=unicode(original_request)[:2048]), )
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, )