# GNU MediaGoblin -- federated, autonomous media hosting # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import six from mediagoblin.db.models import MediaEntry, User, LocalUser from mediagoblin.plugins.archivalook.models import FeaturedMedia from mediagoblin.tools.translate import lazy_pass_to_ugettext as _ from mediagoblin.plugins.archivalook.models import FeaturedMedia def get_media_entry_from_uploader_slug(uploader_username, slug): """ Accepts two strings and searches to see if those strings identify a MediaEntry :param uploader_username A string representing the User.username of the user who uploaded a piece of media. :param slug A string representing the slug of a piece of media :returns media A MediaEntry object or None if no entry matches the specifications. """ uploader = LocalUser.query.filter( LocalUser.username==uploader_username ).first() media = MediaEntry.query.filter( MediaEntry.get_actor == uploader ).filter( MediaEntry.slug == slug).first() return media def parse_url(url): """ A simple helper function that extracts the uploader and slug from a full url :param url A string containing the url for a piece of media. This should be in the format of "/u/{user}/m/{media}/" :returns (uploader_username, slug) Uploader_username is a unicode string representing the username of the user who uploaded the piece of media, slug is the media entry's url slug. """ url = six.text_type(url) u_end, m_start, m_end, end = (url.find('/u/') + 3, url.find('/m/'), url.find('/m/') + 3, url.rfind('/')) uploader_username = url[u_end:m_start].strip() slug = url[m_end:end].strip() return uploader_username, slug def split_featured_media_list(featured_media): """ This script is part of processing post request on the /mod/feature-media/ page. Post requests on these pages will only include the textbox, so this script accepts the textbox's contents as its parameter. :parameter featured_media A string from a submitted textarea within the post request on /mod/feature-media/ :returns all_featured_media A dictionary of the format MediaEntry : 'string' where MediaEntry is a featured piece of media and 'string' is a string representation of its display type (primary, secondary or tertiary) """ featured_media = six.text_type(featured_media) featured_media_list = featured_media.split("\n") display_type = 0 media_already_featured = [] all_featured_media = [] for line in featured_media_list: if line == '' or line.isspace(): continue elif line.startswith(u'-'): display_type += 1 elif display_type <= 0 or display_type > 3: continue else: uploader, slug = parse_url(line) media = get_media_entry_from_uploader_slug(uploader, slug) # Make sure the media entry referenced exists, and has not already # been featured higher up the list if media == None or media in media_already_featured: continue media_already_featured.append(media) all_featured_media.append((media, [None, u'primary', u'secondary', u'tertiary'][display_type])) return all_featured_media def create_featured_media_textbox(): """ This script searches through the database of which media is featured and returns a string of each entry in the proper format for use in the /mod/feature-media/ page. This string will be used as the default text in the textbox on that page. """ primaries = FeaturedMedia.query.order_by( FeaturedMedia.order.asc()).filter( FeaturedMedia.display_type == u'primary').all() secondaries = FeaturedMedia.query.order_by( FeaturedMedia.order.asc()).filter( FeaturedMedia.display_type == u'secondary').all() tertiaries = FeaturedMedia.query.order_by( FeaturedMedia.order.asc()).filter( FeaturedMedia.display_type == u'tertiary').all() output_text = u'' for display_type, feature_list in [ (_(u'Primary'),primaries), (_(u'Secondary'),secondaries), (_(u'Tertiary'),tertiaries)]: output_text += _( u"""-----------{display_type}-Features--------------------------- """).format(display_type=display_type) for feature in feature_list: media_entry = feature.media_entry output_text += u'/u/{uploader_username}/m/{media_slug}/\n'.format( uploader_username = media_entry.get_actor.username, media_slug = media_entry.slug) return output_text def automatically_add_new_feature(media_entry): """ This function automates the addition of a new feature. New features will be placed at the top of the feature stack as 'primary' features. All of the current features are demoted one step to make room. :param media_entry :type mediagoblin.db.MediaEntry The media entry that will been featured which this function targets """ # Set variables to determine which media entries should be pushed down to # maintain the correct number of primary & secondary featured media. At this # point the program assumes that there should be 1 primary feature and 2 # secondary features, but in the future this should be a variable editable # by the site admin. too_many_primaries = FeaturedMedia.query.filter( FeaturedMedia.display_type==u'primary').count() >= 1 too_many_secondaries = FeaturedMedia.query.filter( FeaturedMedia.display_type==u'secondary').count() >= 2 featured_first_to_last = FeaturedMedia.query.order_by( FeaturedMedia.order.asc()).all() for feature in featured_first_to_last: # Some features have the option to demote or promote themselves to a # different display_type, based on their position. But all features move # up and down one step in the stack. if (feature.is_last_of_type() and feature.display_type == u'primary' and too_many_primaries): feature.demote() too_many_primaries = False elif (feature.is_last_of_type() and feature.display_type == u'secondary' and too_many_secondaries): feature.demote() too_many_secondaries = False feature.move_down() feature.save() # Create the new feature at the top of the stack. new_feature = FeaturedMedia( media_entry=media_entry, display_type=u"primary", order=0) new_feature.save() return new_feature def automatically_remove_feature(media_entry): """ This function automates the removal of a feature. All of the features below them are promoted one step to close the gap. :param media_entry :type mediagoblin.db.MediaEntry The media entry that will been removed which this function targets """ # Get the feature which will be deleted target_feature = FeaturedMedia.query.filter( FeaturedMedia.media_entry_id == media_entry.id).first() # Find out which continuing features will have to be adjusted featured_last_to_first = FeaturedMedia.query.filter( FeaturedMedia.order>target_feature.order).order_by( FeaturedMedia.order.desc()).all() for feature in featured_last_to_first: # Maintain the current arrangement of primary/secondary/tertiary # features by moving all the features below the deleted one up on slot # and promoting any features in the proper position. if feature.is_first_of_type(): feature.promote() feature.move_up() feature.save() # Delete the feature, now that it's space has been closed target_feature.delete() def promote_feature(media_entry): """ This function takes a current feature and moves it up the stack so that it will be displayed higher up. It swaps the place of the selected feature for the one above it, or if relevant raises the display_type of the feature up one rung (ie. from 'tertiary' to 'secondary') :param media_entry :type mediagoblin.db.MediaEntry The media entry that has been featured which this function targets """ # Get the FeaturedMedia object target_feature = FeaturedMedia.query.filter( FeaturedMedia.media_entry_id == media_entry.id).first() # Get the first Feature with a lower order than the target above_feature = FeaturedMedia.query.filter( FeaturedMedia.order < target_feature.order).order_by( FeaturedMedia.order.desc()).first() # If the feature is not the uppermost one if above_feature is not None: # Swap the positions of the target feature with the one before it (target_feature.order, above_feature.order) = above_feature.order, target_feature.order (target_feature.display_type, above_feature.display_type) = (above_feature.display_type, target_feature.display_type) above_feature.save() # Change the feature's display type to a more prominent one elif target_feature.display_type == u'secondary': target_feature.display_type = u'primary' elif target_feature.display_type == u'tertiary': target_feature.display_type = u'secondary' target_feature.save() def demote_feature(media_entry): """ This function takes a current feature and moves it down the stack so that it will be displayed lower down. It swaps the place of the selected feature for the one below it, or if relevant lowers the display_type of the feature down one rung (ie. from 'secondary' to 'tertiary') :param media_entry :type mediagoblin.db.MediaEntry The media entry that has been featured which this function targets """ # Get the FeaturedMedia object target_feature = FeaturedMedia.query.filter( FeaturedMedia.media_entry_id == media_entry.id).first() # Get the first Feature with a higher order than the target below_feature = FeaturedMedia.query.filter( FeaturedMedia.order > target_feature.order).order_by( FeaturedMedia.order.asc()).first() # If the feature is not the lowest one if below_feature != None: # Swap the positions of the target feature with the one after it (target_feature.order, below_feature.order) = below_feature.order, target_feature.order (target_feature.display_type, below_feature.display_type) = (below_feature.display_type, target_feature.display_type) below_feature.save() # Change the feature's display type to a less prominent one elif target_feature.display_type == u'secondary': target_feature.display_type = u'tertiary' elif target_feature.display_type == u'primary': target_feature.display_type = u'secondary' target_feature.save()