cms.models.managers: 200 total statements, 0.0% covered

Generated: Wed 2013-03-13 10:33 CET

Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/cms/models/managers.py

Stats: 0 executed, 185 missed, 15 excluded, 301 ignored

  1. # -*- coding: utf-8 -*-
  2. from cms.cache.permissions import get_permission_cache, set_permission_cache
  3. from cms.exceptions import NoPermissionsException
  4. from cms.models.query import PageQuerySet
  5. from cms.publisher import PublisherManager
  6. from cms.utils.i18n import get_fallback_languages
  7. from django.conf import settings
  8. from django.contrib.sites.models import Site
  9. from django.db import models
  10. from django.db.models import Q
  11. class PageManager(PublisherManager):
  12. """Use draft() and public() methods for accessing the corresponding
  13. instances.
  14. """
  15. def get_query_set(self):
  16. """Change standard model queryset to our own.
  17. """
  18. return PageQuerySet(self.model)
  19. def drafts(self):
  20. return super(PageManager, self).drafts().exclude(
  21. publisher_state=self.model.PUBLISHER_STATE_DELETE
  22. )
  23. def public(self):
  24. return super(PageManager, self).public().exclude(
  25. publisher_state=self.model.PUBLISHER_STATE_DELETE
  26. )
  27. # !IMPORTANT: following methods always return access to draft instances,
  28. # take care on what you do one them. use Page.objects.public() for accessing
  29. # the published page versions
  30. # Just some of the queryset methods are implemented here, access queryset
  31. # for more getting more supporting methods.
  32. # TODO: check which from following methods are really required to be on
  33. # manager, maybe some of them can be just accessible over queryset...?
  34. def on_site(self, site=None):
  35. return self.get_query_set().on_site(site)
  36. def root(self):
  37. """
  38. Return a queryset with pages that don't have parents, a.k.a. root. For
  39. current site - used in frontend
  40. """
  41. return self.get_query_set().root()
  42. def all_root(self):
  43. """
  44. Return a queryset with pages that don't have parents, a.k.a. root. For
  45. all sites - used in frontend
  46. """
  47. return self.get_query_set().all_root()
  48. def valid_targets(self, page_id, request, perms, page=None):
  49. """
  50. Give valid targets to move a page into the tree
  51. """
  52. return self.get_query_set().valid_targets(page_id, request, perms, page)
  53. def published(self, site=None):
  54. return self.get_query_set().published(site)
  55. def expired(self):
  56. return self.drafts().expired()
  57. # - seems this is not used anymore...
  58. # def get_pages_with_application(self, path, language):
  59. # """Returns all pages containing application for current path, or
  60. # any parrent. Returned list is sorted by path length, longer path first.
  61. # """
  62. # paths = levelize_path(path)
  63. # q = Q()
  64. # for path in paths:
  65. # # build q for all the paths
  66. # q |= Q(title_set__path=path, title_set__language=language)
  67. # app_pages = self.published().filter(q & Q(title_set__application_urls__gt='')).distinct()
  68. # # add proper ordering
  69. # app_pages.query.order_by.extend(('LENGTH(`cms_title`.`path`) DESC',))
  70. # return app_pages
  71. def get_all_pages_with_application(self):
  72. """Returns all pages containing applications for all sites.
  73. Doesn't cares about the application language.
  74. """
  75. return self.get_query_set().filter(title_set__application_urls__gt='').distinct()
  76. def get_home(self, site=None):
  77. return self.get_query_set().get_home(site)
  78. def search(self, q, language=None, current_site_only=True):
  79. """Simple search function
  80. Plugins can define a 'search_fields' tuple similar to ModelAdmin classes
  81. """
  82. from cms.plugin_pool import plugin_pool
  83. qs = self.get_query_set()
  84. if settings.CMS_MODERATOR:
  85. qs = qs.public()
  86. if current_site_only:
  87. site = Site.objects.get_current()
  88. qs = qs.filter(site=site)
  89. qt = Q(title_set__title__icontains=q)
  90. # find 'searchable' plugins and build query
  91. qp = Q()
  92. plugins = plugin_pool.get_all_plugins()
  93. for plugin in plugins:
  94. cmsplugin = plugin.model
  95. if hasattr(cmsplugin, 'search_fields'):
  96. for field in cmsplugin.search_fields:
  97. qp |= Q(**{'placeholders__cmsplugin__%s__%s__icontains' % \
  98. (cmsplugin.__name__.lower(), field):q})
  99. if language:
  100. qt &= Q(title_set__language=language)
  101. qp &= Q(cmsplugin__language=language)
  102. qs = qs.filter(qt | qp)
  103. return qs.distinct()
  104. class TitleManager(PublisherManager):
  105. def get_title(self, page, language, language_fallback=False):
  106. """
  107. Gets the latest content for a particular page and language. Falls back
  108. to another language if wanted.
  109. """
  110. try:
  111. title = self.get(language=language, page=page)
  112. return title
  113. except self.model.DoesNotExist:
  114. if language_fallback:
  115. try:
  116. titles = self.filter(page=page)
  117. fallbacks = get_fallback_languages(language)
  118. for lang in fallbacks:
  119. for title in titles:
  120. if lang == title.language:
  121. return title
  122. return None
  123. except self.model.DoesNotExist:
  124. pass
  125. else:
  126. raise
  127. return None
  128. def get_page_slug(self, slug, site=None):
  129. """
  130. Returns the latest slug for the given slug and checks if it's available
  131. on the current site.
  132. """
  133. if not site:
  134. site = Site.objects.get_current()
  135. try:
  136. titles = self.filter(
  137. slug=slug,
  138. page__site=site,
  139. ).select_related() # 'page')
  140. except self.model.DoesNotExist:
  141. return None
  142. else:
  143. return titles
  144. # created new public method to meet test case requirement and to get a list of titles for published pages
  145. def public(self):
  146. return self.get_query_set().filter(page__publisher_is_draft=False, page__published=True)
  147. def drafts(self):
  148. return self.get_query_set().filter(page__publisher_is_draft=True)
  149. def set_or_create(self, request, page, form, language):
  150. """
  151. set or create a title for a particular page and language
  152. """
  153. base_fields = [
  154. 'slug',
  155. 'title',
  156. 'meta_description',
  157. 'meta_keywords',
  158. 'page_title',
  159. 'menu_title'
  160. ]
  161. advanced_fields = [
  162. 'application_urls',
  163. 'redirect',
  164. ]
  165. cleaned_data = form.cleaned_data
  166. try:
  167. obj = self.get(page=page, language=language)
  168. except self.model.DoesNotExist:
  169. data = {}
  170. for name in base_fields:
  171. if name in cleaned_data:
  172. data[name] = cleaned_data[name]
  173. data['page'] = page
  174. data['language'] = language
  175. if page.has_advanced_settings_permission(request):
  176. overwrite_url = cleaned_data.get('overwrite_url', None)
  177. if overwrite_url:
  178. data['has_url_overwrite'] = True
  179. data['path'] = overwrite_url
  180. for field in advanced_fields:
  181. value = cleaned_data.get(field, None)
  182. if value:
  183. data[field] = value
  184. return self.create(**data)
  185. for name in base_fields:
  186. value = cleaned_data.get(name, None)
  187. setattr(obj, name, value)
  188. if page.has_advanced_settings_permission(request):
  189. overwrite_url = cleaned_data.get('overwrite_url', None)
  190. obj.has_url_overwrite = bool(overwrite_url)
  191. obj.path = overwrite_url
  192. for field in advanced_fields:
  193. setattr(obj, field, cleaned_data.get(field, None))
  194. obj.save()
  195. return obj
  196. ################################################################################
  197. # Permissions
  198. ################################################################################
  199. class BasicPagePermissionManager(models.Manager):
  200. """Global page permission manager accessible under objects.
  201. !IMPORTANT: take care, PagePermissionManager extends this manager
  202. """
  203. def with_user(self, user):
  204. """Get all objects for given user, also takes look if user is in some
  205. group.
  206. """
  207. return self.filter(Q(user=user) | Q(group__user=user))
  208. def with_can_change_permissions(self, user):
  209. """Set of objects on which user haves can_change_permissions. !But only
  210. the ones on which is this assigned directly. For getting reall
  211. permissions use page.permissions manager.
  212. """
  213. return self.with_user(user).filter(can_change_permissions=True)
  214. class PagePermissionManager(BasicPagePermissionManager):
  215. """Page permission manager accessible under objects.
  216. """
  217. def subordinate_to_user(self, user):
  218. """Get all page permission objects on which user/group is lover in
  219. hierarchy then given user and given user can change permissions on them.
  220. !IMPORTANT, but exclude objects with given user, or any group containing
  221. this user - he can't be able to change his own permissions, because if
  222. he does, and removes some permissions from himself, he will not be able
  223. to add them anymore.
  224. Example:
  225. A
  226. / \
  227. user B,E
  228. / \
  229. C,X D,Y
  230. Gives permission nodes C,X,D,Y under user, so he can edit
  231. permissions if he haves can_change_permission.
  232. Example:
  233. A,Y
  234. / \
  235. user B,E,X
  236. / \
  237. C,X D,Y
  238. Gives permission nodes C,D under user, so he can edit, but not
  239. anymore to X,Y, because this users are on the same level or higher
  240. in page hierarchy. (but only if user have can_change_permission)
  241. Example:
  242. A
  243. / \
  244. user B,E
  245. / \ \
  246. C,X D,Y user
  247. / \
  248. I J,A
  249. User permissions can be assigned to multiple page nodes, so merge of
  250. all of them is required. In this case user can see permissions for
  251. users C,X,D,Y,I,J but not A, because A user in higher in hierarchy.
  252. If permission object holds group, this permission object can be visible
  253. to user only if all of the group members are lover in hierarchy. If any
  254. of members is higher then given user, this entry must stay invisible.
  255. If user is superuser, or haves global can_change_permission permissions,
  256. show him everything.
  257. Result of this is used in admin for page permissions inline.
  258. """
  259. from cms.models import GlobalPagePermission, Page
  260. if user.is_superuser or \
  261. GlobalPagePermission.objects.with_can_change_permissions(user):
  262. # everything for those guys
  263. return self.all()
  264. # get user level
  265. from cms.utils.permissions import get_user_permission_level
  266. try:
  267. user_level = get_user_permission_level(user)
  268. except NoPermissionsException:
  269. return self.none()
  270. # get current site
  271. site = Site.objects.get_current()
  272. # get all permissions
  273. page_id_allow_list = Page.permissions.get_change_permissions_id_list(user, site)
  274. # get permission set, but without objects targeting user, or any group
  275. # in which he can be
  276. qs = self.filter(
  277. page__id__in=page_id_allow_list,
  278. page__level__gte=user_level,
  279. )
  280. qs = qs.exclude(user=user).exclude(group__user=user)
  281. return qs
  282. def for_page(self, page):
  283. """Returns queryset containing all instances somehow connected to given
  284. page. This includes permissions to page itself and permissions inherited
  285. from higher pages.
  286. NOTE: this returns just PagePermission instances, to get complete access
  287. list merge return of this function with Global permissions.
  288. """
  289. from cms.models import ACCESS_DESCENDANTS, ACCESS_CHILDREN,\
  290. ACCESS_PAGE_AND_CHILDREN, ACCESS_PAGE_AND_DESCENDANTS
  291. # code taken from
  292. # https://github.com/divio/django-cms/issues/1113#issuecomment-3376790
  293. q_tree = Q(page__tree_id=page.tree_id)
  294. q_page = Q(page=page)
  295. # NOTE: '... or 0' is used for test cases,
  296. # if the page is not saved through mptt
  297. left_right = {
  298. 'page__%s__lte' % page._mptt_meta.left_attr: getattr(page, page._mptt_meta.left_attr) or 0,
  299. 'page__%s__gte' % page._mptt_meta.right_attr: getattr(page, page._mptt_meta.right_attr) or 0,
  300. }
  301. q_parents = Q(**left_right)
  302. q_desc = (Q(page__level__lt=page.level) & (Q(grant_on=ACCESS_DESCENDANTS) | Q(grant_on=ACCESS_PAGE_AND_DESCENDANTS)))
  303. q_kids = (Q(page__level=page.level - 1) & (Q(grant_on=ACCESS_CHILDREN) | Q(grant_on=ACCESS_PAGE_AND_CHILDREN)))
  304. query = q_tree & q_parents & (q_page | q_desc | q_kids)
  305. return self.filter(query).order_by('page__level')
  306. class PagePermissionsPermissionManager(models.Manager):
  307. """Page permissions permission manager.
  308. !IMPORTANT: this actually points to Page model, not to PagePermission.
  309. Seems this will be better approach. Accessible under permissions.
  310. Maybe this even shouldn't be a manager - it mixes different models together.
  311. """
  312. # we will return this in case we have a superuser, or permissions are not
  313. # enabled/configured in settings
  314. GRANT_ALL = 'All'
  315. def get_publish_id_list(self, user, site):
  316. """
  317. Give a list of page where the user has publish rights or the string "All" if
  318. the user has all rights.
  319. """
  320. return self.__get_id_list(user, site, "can_publish")
  321. def get_change_id_list(self, user, site):
  322. """
  323. Give a list of page where the user has edit rights or the string "All" if
  324. the user has all rights.
  325. """
  326. return self.__get_id_list(user, site, "can_change")
  327. def get_add_id_list(self, user, site):
  328. """
  329. Give a list of page where the user has add page rights or the string
  330. "All" if the user has all rights.
  331. """
  332. return self.__get_id_list(user, site, "can_add")
  333. def get_delete_id_list(self, user, site):
  334. """
  335. Give a list of page where the user has delete rights or the string "All" if
  336. the user has all rights.
  337. """
  338. return self.__get_id_list(user, site, "can_delete")
  339. def get_advanced_settings_id_list(self, user, site):
  340. """
  341. Give a list of page where the user can change advanced settings or the
  342. string "All" if the user has all rights.
  343. """
  344. return self.__get_id_list(user, site, "can_change_advanced_settings")
  345. def get_change_permissions_id_list(self, user, site):
  346. """Give a list of page where the user can change permissions.
  347. """
  348. return self.__get_id_list(user, site, "can_change_permissions")
  349. def get_move_page_id_list(self, user, site):
  350. """Give a list of pages which user can move.
  351. """
  352. return self.__get_id_list(user, site, "can_move_page")
  353. def get_moderate_id_list(self, user, site):
  354. """Give a list of pages which user can moderate. If moderation isn't
  355. installed, nobody can moderate.
  356. """
  357. if not settings.CMS_MODERATOR:
  358. return []
  359. return self.__get_id_list(user, site, "can_moderate")
  360. def get_view_id_list(self, user, site):
  361. """Give a list of pages which user can view.
  362. """
  363. return self.__get_id_list(user, site, "can_view")
  364. '''
  365. def get_change_list_id_list(self, user, site):
  366. """This is used just in admin now. Gives all ids where user haves can_edit
  367. and can_add merged together.
  368. There is for sure a better way how to do this over sql, need to be
  369. optimized...
  370. """
  371. can_change = self.get_change_id_list(user)
  372. can_add = self.get_add_id_list(user)
  373. if can_change is can_add:
  374. # GRANT_ALL case
  375. page_id_list = can_change
  376. else:
  377. permission_set = filter(lambda i: not i is PagePermissionsPermissionManager.GRANT_ALL, [can_change, can_add])
  378. if len(permission_set) is 1:
  379. page_id_list = permission_set[0]
  380. else:
  381. page_id_list = list(set(can_change).union(set(can_add)))
  382. return page_id_list
  383. '''
  384. def __get_id_list(self, user, site, attr):
  385. from cms.models import (GlobalPagePermission, PagePermission,
  386. MASK_PAGE, MASK_CHILDREN, MASK_DESCENDANTS)
  387. if attr != "can_view":
  388. if not user.is_authenticated() or not user.is_staff:
  389. return []
  390. if user.is_superuser or not settings.CMS_PERMISSION:
  391. # got superuser, or permissions aren't enabled? just return grant
  392. # all mark
  393. return PagePermissionsPermissionManager.GRANT_ALL
  394. # read from cache if posssible
  395. cached = get_permission_cache(user, attr)
  396. if cached is not None:
  397. return cached
  398. # check global permissions
  399. global_permissions = GlobalPagePermission.objects.with_user(user)
  400. if global_permissions.filter(**{
  401. attr: True, 'sites__in': [site]
  402. }).exists():
  403. # user or his group are allowed to do `attr` action
  404. # !IMPORTANT: page permissions must not override global permissions
  405. return PagePermissionsPermissionManager.GRANT_ALL
  406. # for standard users without global permissions, get all pages for him or
  407. # his group/s
  408. qs = PagePermission.objects.with_user(user)
  409. qs.order_by('page__tree_id', 'page__level', 'page__lft')
  410. # default is denny...
  411. page_id_allow_list = []
  412. for permission in qs:
  413. if getattr(permission, attr):
  414. # can add is special - we are actually adding page under current page
  415. if permission.grant_on & MASK_PAGE or attr is "can_add":
  416. page_id_allow_list.append(permission.page.id)
  417. if permission.grant_on & MASK_CHILDREN and not attr is "can_add":
  418. page_id_allow_list.extend(permission.page.get_children().values_list('id', flat=True))
  419. elif permission.grant_on & MASK_DESCENDANTS:
  420. page_id_allow_list.extend(permission.page.get_descendants().values_list('id', flat=True))
  421. # store value in cache
  422. set_permission_cache(user, attr, page_id_allow_list)
  423. return page_id_allow_list
  424. class PageModeratorStateManager(models.Manager):
  425. def get_delete_actions(self):
  426. from cms.models import PageModeratorState
  427. return self.filter(action=PageModeratorState.ACTION_DELETE)