Django Library PyGal
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. from ..context import context_adaption
  2. from django.conf import settings
  3. from django.contrib import messages
  4. from django.contrib.auth.decorators import login_required
  5. from django.http import FileResponse, HttpResponseNotAllowed
  6. from django.shortcuts import render, HttpResponse, redirect
  7. from django.urls.base import reverse
  8. from django.utils.translation import gettext as _
  9. from ..forms import TagForm
  10. import fstools
  11. from ..help import help_pages
  12. from .xnail import get_image_instance, other
  13. from .infoviews import get_wrapper_instance as get_infoview_wrapper_instance
  14. import media
  15. import mimetypes
  16. from ..models import get_item_by_rel_path, get_item_type, TYPE_IMAGE, Tag, Item
  17. import os
  18. import pygal
  19. from ..queries import search_result_query
  20. import tempfile
  21. from themes import Context
  22. from users.views import profile_pre_actions, profile_post_actions
  23. from users.forms import UserProfileFormLanguageOnly
  24. from .userviews import get_wrapper_instance as get_userview_wrapper_instance
  25. import zipfile
  26. from pygal.views.userviews import query_view, ItemDoesNotEsistWithinParent
  27. def pygal_item_does_not_exist(request, context):
  28. context.set_additional_title(_('Page does not exist!'))
  29. messages.error(request, _('The Item for the given URL does not exist.'))
  30. return render(request, 'pygal/empty.html', context=context)
  31. def pygal_access_denied(request, context):
  32. context.set_additional_title(_('Access denied!'))
  33. messages.error(request, _('Access Denied: You don\'t have access rights.'))
  34. return render(request, 'pygal/empty.html', context=context)
  35. def get_next(request):
  36. if not request.POST:
  37. return request.GET.get('next', '/')
  38. else:
  39. return request.POST.get('next', '/')
  40. def pygal_responses(request, responsetype=pygal.RESP_TYPE_USERVIEW, datatype=pygal.DATA_TYPE_SEARCH, rel_path=''):
  41. # raw and scaled items
  42. if responsetype in [pygal.RESP_TYPE_RAWITEM, pygal.RESP_TYPE_WEBNAIL, pygal.RESP_TYPE_THUMBNAIL]:
  43. return pygal_item(request, responsetype, rel_path)
  44. # userview and infoview for items
  45. elif responsetype in [pygal.RESP_TYPE_USERVIEW, pygal.RESP_TYPE_INFOVIEW] and datatype == pygal.DATA_TYPE_ITEM:
  46. return pygal_userview_infoview(request, responsetype, rel_path)
  47. # download
  48. elif responsetype == pygal.RESP_TYPE_DOWNLOAD:
  49. return pygal_download(request, datatype, rel_path)
  50. # search
  51. elif responsetype in [pygal.RESP_TYPE_USERVIEW, pygal.RESP_TYPE_INFOVIEW] and datatype == pygal.DATA_TYPE_SEARCH:
  52. if not rel_path:
  53. return pygal_search(request, rel_path)
  54. else:
  55. return pygal_userview_infoview(request, responsetype, rel_path)
  56. else:
  57. d = {
  58. 'responsetype': responsetype,
  59. 'datatype': datatype,
  60. }
  61. messages.error(request, _('No response implemented in pygal.views.pygal_responses for responsetype="%(responsetype)s" and datatype="%(datatype)s".') % d)
  62. return redirect('/')
  63. def pygal_search(request, rel_path):
  64. context = Context(request) # needs to be executed first because of time mesurement
  65. w = query_view(request, search_result_query(request))
  66. context_adaption(
  67. context,
  68. request,
  69. wrapper_instance=w
  70. )
  71. context.set_additional_title(w.name)
  72. return w.render(context)
  73. def pygal_download(request, datatype, rel_path):
  74. full_path = pygal.get_full_path(rel_path)
  75. if os.path.isfile(full_path):
  76. # Download a file
  77. temp = open(full_path, 'rb')
  78. fn = os.path.basename(full_path)
  79. else:
  80. if pygal.is_favouriteview(request) or pygal.is_searchview(request) and rel_path == '':
  81. if pygal.is_favouriteview(request):
  82. fn = 'favourites.zip'
  83. else:
  84. fn = 'search.zip'
  85. # Download the favourites or search results
  86. fp_list = []
  87. for i in search_result_query(request):
  88. if i.may_read(request.user):
  89. fp_list.append(pygal.get_full_path(i.rel_path))
  90. else:
  91. # Download the folder full_path
  92. fn = (os.path.basename(full_path) or 'root') + '.zip'
  93. fp_list = []
  94. for i in get_item_by_rel_path(rel_path).all_itemslist():
  95. if i.may_read(request.user):
  96. fp_list.append(pygal.get_full_path(i.rel_path))
  97. #
  98. # Create the Archive from fp_list
  99. #
  100. flat = pygal.is_flat(request)
  101. if not os.path.exists(settings.TEMP_ROOT):
  102. fstools.mkdir(settings.TEMP_ROOT)
  103. temp = tempfile.TemporaryFile(dir=settings.TEMP_ROOT)
  104. archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_STORED)
  105. for fp in fp_list:
  106. archive.write(fp, os.path.basename(pygal.get_rel_path(fp)) if flat else pygal.get_rel_path(fp))
  107. archive.close()
  108. temp.seek(0)
  109. #
  110. # return file response
  111. #
  112. response = FileResponse(temp, content_type=mimetypes.types_map.get(os.path.splitext(fn)[1]))
  113. response['Content-Disposition'] = 'attachment; filename="%s"' % fn
  114. return response
  115. def pygal_helpview(request, page='main'):
  116. context = Context(request) # needs to be executed first because of time mesurement
  117. help_content = help_pages[page]
  118. context_adaption(
  119. context, # the base context
  120. request, # the request object to be used in context_adaption
  121. title=_('Help'), # the title for the page (template)
  122. current_help_page=page,
  123. )
  124. context['help_content'] = help_content # the help content itself (template)
  125. return render(request, 'pygal/help.html', context=context)
  126. def pygal_userview_infoview(request, responsetype, rel_path):
  127. context = Context(request) # needs to be executed first because of time mesurement
  128. full_path = pygal.get_full_path(rel_path)
  129. try:
  130. if responsetype == pygal.RESP_TYPE_INFOVIEW:
  131. w = get_infoview_wrapper_instance(full_path, request)
  132. else:
  133. w = get_userview_wrapper_instance(full_path, request)
  134. except LookupError:
  135. context_adaption(context, request)
  136. return pygal_item_does_not_exist(request, context)
  137. context_adaption(
  138. context,
  139. request,
  140. rel_path=rel_path,
  141. wrapper_instance=w
  142. )
  143. #
  144. if w.may_read():
  145. context.set_additional_title(w.name)
  146. try:
  147. return w.render(context)
  148. except ItemDoesNotEsistWithinParent:
  149. return redirect('?'.join([request.META['PATH_INFO'].replace(rel_path, ''), request.META['QUERY_STRING']]))
  150. else:
  151. return pygal_access_denied(request, context)
  152. def pygal_item(request, responsetype, rel_path):
  153. full_path = pygal.get_full_path(rel_path)
  154. i = get_item_by_rel_path(rel_path)
  155. if not i.may_read(request.user):
  156. im = other(full_path, request) # This will return a mime icon instead of the image itself
  157. else:
  158. im = get_image_instance(full_path, request)
  159. #
  160. if responsetype == pygal.RESP_TYPE_THUMBNAIL:
  161. return HttpResponse(im.thumbnail_picture(), content_type=im.mime_type_xnails)
  162. elif responsetype == pygal.RESP_TYPE_WEBNAIL:
  163. return HttpResponse(im.webnail_picture(), content_type=im.mime_type_xnails)
  164. else:
  165. if i.may_read(request.user):
  166. mimetypes.init()
  167. mime_type = mimetypes.types_map.get(os.path.splitext(full_path)[1])
  168. data = open(full_path, 'rb').read()
  169. return HttpResponse(data, content_type=mime_type)
  170. else:
  171. im = media.image(os.path.join(os.path.dirname(__file__), 'forbidden.png'))
  172. if responsetype == pygal.RESP_TYPE_THUMBNAIL:
  173. im.resize(int(pygal.get_thumbnail_size(request) * .75))
  174. return HttpResponse(im.image_data(), content_type='image/png')
  175. @login_required
  176. def pygal_profile(request):
  177. context = Context(request) # needs to be executed first because of time mesurement
  178. current_thumbnail_size = pygal.get_thumbnail_size(request)
  179. current_webnail_size = pygal.get_webnail_size(request)
  180. profile_pre_actions(request, context, UserProfileFormLanguageOnly)
  181. context_adaption(
  182. context,
  183. request,
  184. title=_('Profile for %(username)s') % {'username': request.user.username},
  185. #
  186. THUMBNAIL_SIZES=settings.THUMBNAIL_SIZES,
  187. thumbnail_size=current_thumbnail_size,
  188. WEBNAIL_SIZES=settings.WEBNAIL_SIZES,
  189. webnail_size=current_webnail_size
  190. )
  191. if request.POST:
  192. # store thumbnail size, if changed
  193. thumbnail_size = int(request.POST.get('thumbnail_size'))
  194. if current_thumbnail_size != thumbnail_size:
  195. pygal.set_thumbnail_size(request, thumbnail_size)
  196. current_thumbnail_size = thumbnail_size
  197. messages.info(request, _('The Thumbnail Size was set to "%d"') % (thumbnail_size))
  198. # store webnail size, if changed
  199. webnail_size = int(request.POST.get('webnail_size'))
  200. if current_webnail_size != webnail_size:
  201. pygal.set_webnail_size(request, webnail_size)
  202. current_webnail_size = webnail_size
  203. messages.info(request, _('The Webnail Size was set to "%d"') % (webnail_size))
  204. return profile_post_actions(request, context)
  205. else:
  206. return render(request, 'pygal/profile.html', context=context)
  207. def tag_context_adaption(context, w, request, enable_tag_area_selection):
  208. context['item'] = w
  209. context['next'] = get_next(request)
  210. context['enable_tag_area_selection'] = enable_tag_area_selection
  211. if enable_tag_area_selection:
  212. i_w, i_h = w.width, w.height
  213. max_size = int(pygal.get_webnail_size(request) / 2)
  214. factor_to_original = max(i_w, i_h) / max_size
  215. context['factor_to_original'] = factor_to_original
  216. context['tag_img_width'] = int(i_w / factor_to_original)
  217. context['tag_img_height'] = int(i_h / factor_to_original)
  218. def pygal_addtag(request, rel_path):
  219. context = Context(request) # needs to be executed first because of time mesurement
  220. full_path = pygal.get_full_path(rel_path)
  221. w = get_userview_wrapper_instance(full_path, request)
  222. if not w.may_modify():
  223. messages.error(request, _('You don\'t have the rights to add a Tag to this Item.'))
  224. return redirect(get_next(request))
  225. else:
  226. context_adaption(
  227. context,
  228. request,
  229. rel_path=rel_path,
  230. title=_('Add Tag for %s') % w.name
  231. )
  232. tag_context_adaption(context, w, request, get_item_type(full_path) in [TYPE_IMAGE])
  233. if request.POST:
  234. tag = Tag(item=w.item)
  235. form = TagForm(request.POST, instance=tag, factor_to_original=context.get('factor_to_original'))
  236. if form.is_valid():
  237. form.save()
  238. messages.info(request, _('Thanks for adding a Tag to %s') % w.name)
  239. return redirect(get_next(request))
  240. else:
  241. form = TagForm(factor_to_original=context.get('factor_to_original'))
  242. context['form'] = form
  243. return render(request, 'pygal/tagedit.html', context=context)
  244. def pygal_tagedit(request, tag_id):
  245. context = Context(request) # needs to be executed first because of time mesurement
  246. try:
  247. t = Tag.objects.get(pk=tag_id)
  248. except Tag.DoesNotExist:
  249. messages.error(request, _('Tag %d does not exist!') % tag_id)
  250. return redirect(get_next(request))
  251. if not t.item.may_modify(request.user):
  252. messages.error(request, _('You don\'t have the rights to change Tag %(tag_id)d.') % {'tag_id': tag_id})
  253. return redirect(get_next(request))
  254. else:
  255. w = get_userview_wrapper_instance(pygal.get_full_path(t.item.rel_path), request)
  256. context_adaption(
  257. context,
  258. request,
  259. rel_path=t.item.rel_path,
  260. title=_('Edit Tag %(tag_id)d for %(item_name)s') % {'tag_id': tag_id, 'item_name': w.name}
  261. )
  262. tag_context_adaption(context, w, request, t.item.type in [TYPE_IMAGE])
  263. context['enable_delete_button'] = True
  264. if request.POST:
  265. if request.POST.get('save'):
  266. form = TagForm(request.POST, instance=t, factor_to_original=context.get('factor_to_original'))
  267. if form.is_valid():
  268. form.save()
  269. messages.info(request, _('Thanks for editing Tag %(tag_id)d to %(item_name)s') % {'tag_id': tag_id, 'item_name': w.name})
  270. return redirect(get_next(request))
  271. else:
  272. t.delete()
  273. messages.info(request, _('Tag %(tag_id)d of %(item_name)s has been deleted.') % {'tag_id': tag_id, 'item_name': w.name})
  274. return redirect(get_next(request))
  275. else:
  276. form = TagForm(instance=t, factor_to_original=context.get('factor_to_original'))
  277. context['form'] = form
  278. return render(request, 'pygal/tagedit.html', context=context)
  279. def pygal_favourite(request, set_favourite, rel_path):
  280. nxt = request.GET.get('next', '/')
  281. item = get_item_by_rel_path(rel_path)
  282. if not set_favourite and request.user in item.favourite_of.all():
  283. item.favourite_of.remove(request.user)
  284. item.save()
  285. if nxt.startswith(pygal.url_favouriteview(request)):
  286. nxt = pygal.url_favouriteview(request)
  287. elif set_favourite and request.user not in item.favourite_of.all():
  288. item.favourite_of.add(request.user)
  289. item.save()
  290. else:
  291. messages.error(request, 'Favourite setting is already in targetstate!')
  292. return redirect(nxt + '#%s' % item.name)