tools.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. # GNU MediaGoblin -- federated, autonomous media hosting
  2. # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU Affero General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. import six
  17. from mediagoblin.db.models import MediaEntry, User
  18. from mediagoblin.plugins.archivalook.models import FeaturedMedia
  19. from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
  20. from mediagoblin.plugins.archivalook.models import FeaturedMedia
  21. def get_media_entry_from_uploader_slug(uploader_username, slug):
  22. """
  23. Accepts two strings and searches to see if those strings identify a
  24. MediaEntry
  25. :param uploader_username A string representing the User.username
  26. of the user who uploaded a piece of
  27. media.
  28. :param slug A string representing the slug of a
  29. piece of media
  30. :returns media A MediaEntry object or None if no entry
  31. matches the specifications.
  32. """
  33. uploader = User.query.filter(
  34. User.username == uploader_username).first()
  35. media = MediaEntry.query.filter(
  36. MediaEntry.get_uploader == uploader ).filter(
  37. MediaEntry.slug == slug).first()
  38. return media
  39. def parse_url(url):
  40. """
  41. A simple helper function that extracts the uploader and slug from a full url
  42. :param url A string containing the url for a piece
  43. of media. This should be in the format
  44. of "/u/{user}/m/{media}/"
  45. :returns (uploader_username, slug) Uploader_username is a unicode string
  46. representing the username of the user
  47. who uploaded the piece of media, slug is
  48. the media entry's url slug.
  49. """
  50. url = six.text_type(url)
  51. u_end, m_start, m_end, end = (url.find('/u/') + 3,
  52. url.find('/m/'),
  53. url.find('/m/') + 3,
  54. url.rfind('/'))
  55. uploader_username = url[u_end:m_start].strip()
  56. slug = url[m_end:end].strip()
  57. return uploader_username, slug
  58. def split_featured_media_list(featured_media):
  59. """
  60. This script is part of processing post request on the /mod/feature-media/
  61. page. Post requests on these pages will only include the textbox, so this
  62. script accepts the textbox's contents as its parameter.
  63. :parameter featured_media A string from a submitted
  64. textarea within the post request
  65. on /mod/feature-media/
  66. :returns all_featured_media A dictionary of the format
  67. MediaEntry : 'string'
  68. where MediaEntry is a featured
  69. piece of media and 'string' is
  70. a string representation of its
  71. display type (primary, secondary
  72. or tertiary)
  73. """
  74. featured_media = six.text_type(featured_media)
  75. featured_media_list = featured_media.split("\n")
  76. display_type = 0
  77. media_already_featured = []
  78. all_featured_media = []
  79. for line in featured_media_list:
  80. if line == '' or line.isspace(): continue
  81. elif line.startswith(u'-'):
  82. display_type += 1
  83. elif display_type <= 0 or display_type > 3: continue
  84. else:
  85. uploader, slug = parse_url(line)
  86. media = get_media_entry_from_uploader_slug(uploader, slug)
  87. # Make sure the media entry referenced exists, and has not already
  88. # been featured higher up the list
  89. if media == None or media in media_already_featured: continue
  90. media_already_featured.append(media)
  91. all_featured_media.append((media,
  92. [None,
  93. u'primary',
  94. u'secondary',
  95. u'tertiary'][display_type]))
  96. return all_featured_media
  97. def create_featured_media_textbox():
  98. """
  99. This script searches through the database of which media is featured and
  100. returns a string of each entry in the proper format for use in the
  101. /mod/feature-media/ page. This string will be used as the default text in
  102. the textbox on that page.
  103. """
  104. primaries = FeaturedMedia.query.order_by(
  105. FeaturedMedia.order.asc()).filter(
  106. FeaturedMedia.display_type == u'primary').all()
  107. secondaries = FeaturedMedia.query.order_by(
  108. FeaturedMedia.order.asc()).filter(
  109. FeaturedMedia.display_type == u'secondary').all()
  110. tertiaries = FeaturedMedia.query.order_by(
  111. FeaturedMedia.order.asc()).filter(
  112. FeaturedMedia.display_type == u'tertiary').all()
  113. output_text = u''
  114. for display_type, feature_list in [
  115. (_(u'Primary'),primaries),
  116. (_(u'Secondary'),secondaries),
  117. (_(u'Tertiary'),tertiaries)]:
  118. output_text += _(
  119. u"""-----------{display_type}-Features---------------------------
  120. """).format(display_type=display_type)
  121. for feature in feature_list:
  122. media_entry = feature.media_entry
  123. output_text += u'/u/{uploader_username}/m/{media_slug}/\n'.format(
  124. uploader_username = media_entry.get_uploader.username,
  125. media_slug = media_entry.slug)
  126. return output_text
  127. def automatically_add_new_feature(media_entry):
  128. """
  129. This function automates the addition of a new feature. New features will be
  130. placed at the top of the feature stack as 'primary' features. All of the
  131. current features are demoted one step to make room.
  132. :param media_entry :type mediagoblin.db.MediaEntry
  133. The media entry that will been
  134. featured which this function
  135. targets
  136. """
  137. # Set variables to determine which media entries should be pushed down to
  138. # maintain the correct number of primary & secondary featured media. At this
  139. # point the program assumes that there should be 1 primary feature and 2
  140. # secondary features, but in the future this should be a variable editable
  141. # by the site admin.
  142. too_many_primaries = FeaturedMedia.query.filter(
  143. FeaturedMedia.display_type==u'primary').count() >= 1
  144. too_many_secondaries = FeaturedMedia.query.filter(
  145. FeaturedMedia.display_type==u'secondary').count() >= 2
  146. featured_first_to_last = FeaturedMedia.query.order_by(
  147. FeaturedMedia.order.asc()).all()
  148. for feature in featured_first_to_last:
  149. # Some features have the option to demote or promote themselves to a
  150. # different display_type, based on their position. But all features move
  151. # up and down one step in the stack.
  152. if (feature.is_last_of_type() and feature.display_type == u'primary'
  153. and too_many_primaries):
  154. feature.demote()
  155. too_many_primaries = False
  156. elif (feature.is_last_of_type() and feature.display_type == u'secondary'
  157. and too_many_secondaries):
  158. feature.demote()
  159. too_many_secondaries = False
  160. feature.move_down()
  161. feature.save()
  162. # Create the new feature at the top of the stack.
  163. new_feature = FeaturedMedia(
  164. media_entry=media_entry,
  165. display_type=u"primary",
  166. order=0)
  167. new_feature.save()
  168. return new_feature
  169. def automatically_remove_feature(media_entry):
  170. """
  171. This function automates the removal of a feature. All of the features below
  172. them are promoted one step to close the gap.
  173. :param media_entry :type mediagoblin.db.MediaEntry
  174. The media entry that will been
  175. removed which this function
  176. targets
  177. """
  178. # Get the feature which will be deleted
  179. target_feature = FeaturedMedia.query.filter(
  180. FeaturedMedia.media_entry_id == media_entry.id).first()
  181. # Find out which continuing features will have to be adjusted
  182. featured_last_to_first = FeaturedMedia.query.filter(
  183. FeaturedMedia.order>target_feature.order).order_by(
  184. FeaturedMedia.order.desc()).all()
  185. for feature in featured_last_to_first:
  186. # Maintain the current arrangement of primary/secondary/tertiary
  187. # features by moving all the features below the deleted one up on slot
  188. # and promoting any features in the proper position.
  189. if feature.is_first_of_type():
  190. feature.promote()
  191. feature.move_up()
  192. feature.save()
  193. # Delete the feature, now that it's space has been closed
  194. target_feature.delete()
  195. def promote_feature(media_entry):
  196. """
  197. This function takes a current feature and moves it up the stack so that it
  198. will be displayed higher up. It swaps the place of the selected feature for
  199. the one above it, or if relevant raises the display_type of the feature up
  200. one rung (ie. from 'tertiary' to 'secondary')
  201. :param media_entry :type mediagoblin.db.MediaEntry
  202. The media entry that has been
  203. featured which this function
  204. targets
  205. """
  206. # Get the FeaturedMedia object
  207. target_feature = FeaturedMedia.query.filter(
  208. FeaturedMedia.media_entry_id == media_entry.id).first()
  209. # Get the first Feature with a lower order than the target
  210. above_feature = FeaturedMedia.query.filter(
  211. FeaturedMedia.order < target_feature.order).order_by(
  212. FeaturedMedia.order.desc()).first()
  213. # If the feature is not the uppermost one
  214. if above_feature is not None:
  215. # Swap the positions of the target feature with the one before it
  216. (target_feature.order,
  217. above_feature.order) = above_feature.order, target_feature.order
  218. (target_feature.display_type,
  219. above_feature.display_type) = (above_feature.display_type,
  220. target_feature.display_type)
  221. above_feature.save()
  222. # Change the feature's display type to a more prominent one
  223. elif target_feature.display_type == u'secondary':
  224. target_feature.display_type = u'primary'
  225. elif target_feature.display_type == u'tertiary':
  226. target_feature.display_type = u'secondary'
  227. target_feature.save()
  228. def demote_feature(media_entry):
  229. """
  230. This function takes a current feature and moves it down the stack so that it
  231. will be displayed lower down. It swaps the place of the selected feature for
  232. the one below it, or if relevant lowers the display_type of the feature down
  233. one rung (ie. from 'secondary' to 'tertiary')
  234. :param media_entry :type mediagoblin.db.MediaEntry
  235. The media entry that has been
  236. featured which this function
  237. targets
  238. """
  239. # Get the FeaturedMedia object
  240. target_feature = FeaturedMedia.query.filter(
  241. FeaturedMedia.media_entry_id == media_entry.id).first()
  242. # Get the first Feature with a higher order than the target
  243. below_feature = FeaturedMedia.query.filter(
  244. FeaturedMedia.order > target_feature.order).order_by(
  245. FeaturedMedia.order.asc()).first()
  246. # If the feature is not the lowest one
  247. if below_feature != None:
  248. # Swap the positions of the target feature with the one after it
  249. (target_feature.order,
  250. below_feature.order) = below_feature.order, target_feature.order
  251. (target_feature.display_type,
  252. below_feature.display_type) = (below_feature.display_type,
  253. target_feature.display_type)
  254. below_feature.save()
  255. # Change the feature's display type to a less prominent one
  256. elif target_feature.display_type == u'secondary':
  257. target_feature.display_type = u'tertiary'
  258. elif target_feature.display_type == u'primary':
  259. target_feature.display_type = u'secondary'
  260. target_feature.save()