def testMulti(self): assert_pformat_equal(len(LocalSyncCache.CACHES), 0) c1 = LocalSyncCache(id="test1") assert_pformat_equal(len(LocalSyncCache.CACHES), 1) c2 = LocalSyncCache(id="test1", unique_ids=False) assert_pformat_equal(len(LocalSyncCache.CACHES), 2) c1["c1"] = "foo" c2["c2"] = "bar" assert_pformat_equal(c1, {"c1": "foo"}) assert_pformat_equal(c2, {"c2": "bar"}) c1.check_state() c2.check_state() assert_pformat_equal(c1, {"c1": "foo"}) assert_pformat_equal(c2, {"c2": "bar"}) c1.clear() assert_pformat_equal(c1, {}) # In a "new request" all the same caches should be cleared c2.check_state() assert_pformat_equal(c2, {}) cache_information = LocalSyncCache.get_cache_information() assert_pformat_equal(len(cache_information), 2)
def testMulti(self): self.assertEqual(len(LocalSyncCache.CACHES), 0) c1 = LocalSyncCache(id="test1") self.assertEqual(len(LocalSyncCache.CACHES), 1) c2 = LocalSyncCache(id="test1", unique_ids=False) self.assertEqual(len(LocalSyncCache.CACHES), 2) c1["c1"] = "foo" c2["c2"] = "bar" self.assertEqual(c1, {'c1': 'foo'}) self.assertEqual(c2, {'c2': 'bar'}) c1.check_state() c2.check_state() self.assertEqual(c1, {'c1': 'foo'}) self.assertEqual(c2, {'c2': 'bar'}) c1.clear() self.assertEqual(c1, {}) # In a "new request" all the same caches should be cleared c2.check_state() self.assertEqual(c2, {}) cache_information = LocalSyncCache.get_cache_information() self.assertEqual(len(cache_information), 2)
def testUniqueID(self): with LoggingBuffer("django_tools.local_sync_cache") as log: c1 = LocalSyncCache(id="test1") log.clear() c2 = LocalSyncCache(id="test1") self.assertIn( "ID 'test1' was already used! It must be unique! (Existing ids are: ['test1'])", log.get_messages())
def testUniqueID(self): LocalSyncCache(id="test1") with self.assertLogs(logger="django_tools.local_sync_cache") as logs: LocalSyncCache(id="test1") assert_in_logs( logs, line=( "ERROR:django_tools.local_sync_cache:" "ID 'test1' was already used! It must be unique! (Existing ids are: ['test1'])" ) )
def testBasic(self): c = LocalSyncCache(id="test1") c["key1"] = "value1" assert_pformat_equal(c, {"key1": "value1"}) cache.set("test1", time.time()) c.check_state() assert_pformat_equal(c, {}) cache_information = LocalSyncCache.get_cache_information() assert_pformat_equal(len(cache_information), 1)
def testBasic(self): c = LocalSyncCache(id="test1") c["key1"] = "value1" self.assertEqual(c, {'key1': 'value1'}) cache.set("test1", time.time()) c.check_state() self.assertEqual(c, {}) cache_information = LocalSyncCache.get_cache_information() self.assertEqual(len(cache_information), 1)
def get_from_host(self, current_host): if self.ALIAS_CACHE is None: # first request after startup / model.save() -> fill the cache logger.debug("init LocalSyncCache for SiteAlias cache") self.ALIAS_CACHE = LocalSyncCache(id="SiteAliasCache") if "string" not in self.ALIAS_CACHE: logger.debug("Fill SiteAlias cache") self.ALIAS_CACHE["string"] = {} self.ALIAS_CACHE["regex"] = [] queryset = self.all() for site_alias in queryset: site = site_alias.site alias = site_alias.alias if site_alias.regex: regex = compile_alias(alias) self.ALIAS_CACHE["regex"].append((regex, site)) else: self.ALIAS_CACHE["string"][alias.lower()] = site self.ALIAS_CACHE["regex"] = tuple(self.ALIAS_CACHE["regex"]) logger.debug("Alias string cache: %s" % repr(self.ALIAS_CACHE["string"])) logger.debug("Alias regex cache: %s" % repr(self.ALIAS_CACHE["regex"])) # Try first all string alias: try: return self.ALIAS_CACHE["string"][current_host] except KeyError: logger.debug("No site found in string cache for %r" % current_host) # Try all regex alias: for regex, site in self.ALIAS_CACHE["regex"]: match = regex.search(current_host) if match is not None: return site logger.debug("No site found in regex cache for %r" % current_host) raise self.model.DoesNotExist("No alias found for %r" % current_host)
from django_tools import model_utils from django_tools.local_sync_cache.local_sync_cache import LocalSyncCache from django_tools.models import UpdateInfoBaseModel from django_tools.utils import installed_apps_utils from django_tools.utils.messages import failsafe_message from pylucid_project.apps.pylucid.fields import RootAppChoiceField from pylucid_project.apps.pylucid.signals_handlers import update_plugin_urls from pylucid_project.base_models.base_models import BaseModelManager, BaseModel from pylucid_project.system.pylucid_plugins import PYLUCID_PLUGINS TAG_INPUT_HELP_URL = \ "http://google.com/search?q=cache:django-tagging.googlecode.com/files/tagging-0.2-overview.html#tag-input" _URL_RESOLVER_CACHE = LocalSyncCache(id="PluginPage_url_resolver") class PluginPageManager(BaseModelManager): """ TODO: In next release witch has a update routine: we should switch: from plugin_instance.installed_apps_string to plugin_instance.pkg_string """ _APP_CHOICES = None def get_app_choices(self): """ Generate a choice list with all views witch can handle a empty root url. But PyLucid can only handle own plugins, see: http://trac.pylucid.net/ticket/333 """
def __unicode__(self): # print "DynamicSiteId __unicode__" return str(SITE_THREAD_LOCAL.SITE_ID) def _clear_cache(self): logger.debug("Clear SITE_CACHE (The django-tools LocalSyncCache() dict)") SITE_CACHE.clear() if USE_DYNAMIC_SITE_MIDDLEWARE == True: # Use the same SITE_CACHE for getting site object by host [1] and get current site by SITE_ID [2] # [1] here in DynamicSiteMiddleware._get_site_id_from_host() # [2] in django.contrib.sites.models.SiteManager.get_current() SITE_CACHE = LocalSyncCache(id="DynamicSiteMiddlewareCache") sites_models.SITE_CACHE = SITE_CACHE SITE_THREAD_LOCAL = local() # Use Fallback ID if host not exist in Site table. We use int() here, because # os environment variables are always strings. FALLBACK_SITE_ID = int(getattr(os.environ, "SITE_ID", settings.SITE_ID)) logger.debug("Fallback SITE_ID: %r" % FALLBACK_SITE_ID) # Use Fallback ID at startup before process_request(), e.g. in unittests SITE_THREAD_LOCAL.SITE_ID = FALLBACK_SITE_ID try: FALLBACK_SITE = Site.objects.get(id=FALLBACK_SITE_ID) except Site.DoesNotExist as e:
class PageMeta(BaseModel, UpdateInfoBaseModel, PermissionsBase): """ Meta data for PageContent or PluginPage inherited attributes from UpdateInfoBaseModel: createtime -> datetime of creation lastupdatetime -> datetime of the last change createby -> ForeignKey to user who created this entry lastupdateby -> ForeignKey to user who has edited this entry inherited from PermissionsBase: validate_permit_group() check_sub_page_permissions() """ objects = PageMetaManager() on_site = CurrentSiteManager() pagetree = models.ForeignKey("pylucid.PageTree") # Should we add null=True, blank=True here? see clean_fields() below language = models.ForeignKey("pylucid.Language") name = models.CharField(blank=True, max_length=150, help_text="Sort page name (for link text in e.g. menu)" ) title = models.CharField(blank=True, max_length=256, help_text="A long page title (for e.g. page title or link title text)" ) tags = jQueryTagModelField() # a django-tagging model field modified by django-tools keywords = models.CharField(blank=True, max_length=255, help_text="Keywords for the html header. (separated by commas)" ) description = models.CharField(blank=True, max_length=255, help_text="For html header") robots = models.CharField(blank=True, max_length=255, default="index,follow", help_text="for html 'robots' meta content." ) permitViewGroup = models.ForeignKey(Group, related_name="%(class)s_permitViewGroup", help_text="Limit viewable this page in this language to a user group?", null=True, blank=True, ) # FIXME: Add permitEditGroup, too. # e.g.: allow only usergroup X to edit this page in language Y # https://github.com/jedie/PyLucid/issues/57 def clean_fields(self, exclude): super(PageMeta, self).clean_fields(exclude) message_dict = {} try: # We can only check the sub pages, if exists ;) pagetree = self.pagetree except ObjectDoesNotExist: # FIXME: Should self.pagetree() field has null=True, blank=True ? return # Prevents that a unprotected page created below a protected page. # TODO: Check this in unittests # validate_permit_group() method inherited from PermissionsBase self.validate_permit_group("permitViewGroup", exclude, message_dict) # Warn user if PageMeta permissions mismatch with sub pages # TODO: Check this in unittests queryset = PageMeta.objects.filter(pagetree__parent=self.pagetree) self.check_sub_page_permissions(# method inherited from PermissionsBase ("permitViewGroup",), # TODO: permitEditGroup, read above exclude, message_dict, queryset ) if message_dict: raise ValidationError(message_dict) def recusive_attribute(self, attribute): """ Goes the pagetree back to root and return the first match of attribute if not None. used e.g. with permitViewGroup and permitEditGroup from self.validate_permit_group() and self.check_sub_page_permissions() """ parent_pagetree = self.pagetree.parent if parent_pagetree is None: # parent is the tree root return None request = ThreadLocal.get_current_request() if request is None: # Check only if we are in a request return queryset = PageMeta.objects.filter(pagetree=parent_pagetree) parent_pagemeta = None languages = request.PYLUCID.languages # languages are in client prefered order for language in languages: try: parent_pagemeta = queryset.get(language=language) except PageMeta.DoesNotExist: continue else: break if parent_pagemeta is None: return if getattr(parent_pagemeta, attribute) is not None: # the attribute was set by parent page return parent_pagemeta else: # go down to root return parent_pagemeta.recusive_attribute(attribute) _url_cache = LocalSyncCache(id="PageMeta_absolute_url") def get_absolute_url(self): """ absolute url *with* language code (without domain/host part) """ if self.pk in self._url_cache: #print "PageMeta url cache len: %s, pk: %s" % (len(self._url_cache), self.pk) return self._url_cache[self.pk] lang_code = self.language.code page_url = self.pagetree.get_absolute_url() url = "/" + lang_code + page_url self._url_cache[self.pk] = url return url _permalink_cache = LocalSyncCache(id="PageMeta_permalink") def get_permalink(self): """ return a permalink. Use page slug/name/title or nothing as additional text. """ if self.pk in self._permalink_cache: #print "PageMeta permalink_cache len: %s, pk: %s" % (len(self._permalink_cache), self.pk) return self._permalink_cache[self.pk] # Get the system preferences request = ThreadLocal.get_current_request() sys_pref = request.PYLUCID.preferences sys_pref_form = request.PYLUCID.preferences_form use_additions = sys_pref.get("permalink_additions", sys_pref_form.PERMALINK_USE_TITLE) do_slugify = False if use_additions == sys_pref_form.PERMALINK_USE_TITLE: # Append the PageMeta title (language dependent) addition_txt = self.get_title() do_slugify = True elif use_additions == sys_pref_form.PERMALINK_USE_NAME: addition_txt = self.get_name() do_slugify = True elif use_additions == sys_pref_form.PERMALINK_USE_SLUG: addition_txt = self.pagetree.slug else: addition_txt = "" if do_slugify: addition_txt = slugify(addition_txt) url = reverse('PyLucid-permalink', kwargs={'page_id': self.pagetree.id, 'url_rest': addition_txt}) self._permalink_cache[self.pk] = url return url def save(self, *args, **kwargs): """ reset PageMeta and PageTree url cache """ # Clean the local url cache dict self._url_cache.clear() self._permalink_cache.clear() self.pagetree._url_cache.clear() # FIXME: We must only update the cache for the current SITE not for all sites. try: cache.smooth_update() # Save "last change" timestamp in django-tools SmoothCacheBackend except AttributeError: # No SmoothCacheBackend used -> clean the complete cache cache.clear() return super(PageMeta, self).save(*args, **kwargs) def get_site(self): """ used e.g. for self.get_absolute_uri() and the admin page """ return self.pagetree.site get_site.short_description = _('on site') get_site.allow_tags = False def get_other_languages(self): return PageMeta.objects.all().filter(pagetree=self.pagetree).exclude(language=self.language) def get_title(self): """ The page title is optional, if not exist, used the slug from the page tree """ return self.title or self.get_name() def get_name(self): return self.name or self.pagetree.slug def __unicode__(self): return u"PageMeta for page: %r (lang: %s, site: %s)" % ( self.pagetree.slug, self.language.code, self.get_site().domain ) class Meta: app_label = 'pylucid' verbose_name_plural = verbose_name = "PageMeta" unique_together = (("pagetree", "language"),) ordering = ("-lastupdatetime",)
def testLocalSyncCacheMiddleware(self): middleware = LocalSyncCacheMiddleware() c1 = LocalSyncCache(id="testLocalSyncCacheMiddleware1") c2 = LocalSyncCache(id="testLocalSyncCacheMiddleware1", unique_ids=False) c3 = LocalSyncCache(id="testLocalSyncCacheMiddleware2") assert_pformat_equal(len(LocalSyncCache.CACHES), 3) c1["c1"] = "foo" c2["c2"] = "bar" c3["foo"] = "bar" middleware.process_request(None) assert_pformat_equal(c1, {"c1": "foo"}) assert_pformat_equal(c2, {"c2": "bar"}) assert_pformat_equal(c3, {"foo": "bar"}) c1.clear() assert_pformat_equal(c1, {}) # In a "new request" all the same caches should be cleared middleware.process_request(None) assert_pformat_equal(c2, {}) # Other caches should be not affected by clear() assert_pformat_equal(c3, {"foo": "bar"}) c1["c1"] = "foo2" c2["c2"] = "bar2" middleware.process_request(None) assert_pformat_equal(c1, {"c1": "foo2"}) assert_pformat_equal(c2, {"c2": "bar2"}) assert_pformat_equal(c3, {"foo": "bar"}) c2.clear() assert_pformat_equal(c2, {}) # In a "new request" all the same caches should be cleared middleware.process_request(None) assert_pformat_equal(c1, {}) # print LocalSyncCache.pformat_cache_information() cache_information = LocalSyncCache.get_cache_information() assert_pformat_equal(len(cache_information), 3) for item in cache_information: instance = item["instance"] assert_pformat_equal(instance.request_counter, 4) if instance.id == "testLocalSyncCacheMiddleware2": assert_pformat_equal(instance.own_clear_counter, 0) assert_pformat_equal(instance.ext_clear_counter, 0) assert_pformat_equal(instance, {"foo": "bar"}) else: assert_pformat_equal(instance.own_clear_counter, 1) assert_pformat_equal(instance.ext_clear_counter, 1) assert_pformat_equal(instance, {})
def testPformatCacheInfo(self): LocalSyncCache(id="FooBar") txt = LocalSyncCache.pformat_cache_information() self.assertTrue("id: FooBar" in txt) self.assertTrue("instance: {}" in txt) self.assertTrue("cleared: False" in txt)
def testUniqueID(self): c1 = LocalSyncCache(id="test1") self.assertRaises(AssertionError, LocalSyncCache, id="test1")
class PageTree(BaseModel, BaseTreeModel, UpdateInfoBaseModel, PermissionsBase): """ The CMS page tree inherited attributes from TreeBaseModel: parent position inherited attributes from UpdateInfoBaseModel: createtime -> datetime of creation lastupdatetime -> datetime of the last change createby -> ForeignKey to user who creaded this entry lastupdateby -> ForeignKey to user who has edited this entry inherited from PermissionsBase: validate_permit_group() check_sub_page_permissions() """ PAGE_TYPE = 'C' PLUGIN_TYPE = 'P' TYPE_CHOICES = ( (PAGE_TYPE, 'CMS-Page'), (PLUGIN_TYPE, 'PluginPage'), ) TYPE_DICT = dict(TYPE_CHOICES) objects = PageTreeManager() slug = models.SlugField(unique=False, help_text="(for building URLs)") site = models.ForeignKey(Site, default=Site.objects.get_current) on_site = CurrentSiteManager() page_type = models.CharField(max_length=1, choices=TYPE_CHOICES) design = models.ForeignKey("pylucid.Design", help_text="Page Template, CSS/JS files") showlinks = models.BooleanField( default=True, help_text= "Accessable for all users, but don't put a Link to this page into menu/sitemap etc." ) permitViewGroup = models.ForeignKey( Group, related_name="%(class)s_permitViewGroup", help_text="Limit viewable to a group?", null=True, blank=True, ) permitEditGroup = models.ForeignKey( Group, related_name="%(class)s_permitEditGroup", help_text="Usergroup how can edit this page.", null=True, blank=True, ) def clean_fields(self, exclude): """ We must call clean_slug() here, because it needs a queryset. """ # check if parent is the same entry: child <-> parent loop: super(PageTree, self).clean_fields(exclude) message_dict = {} # Check if slug exist in the same sub tree: if "slug" not in exclude: if self.parent == None: # parent is the tree root if self.slug in settings.SLUG_BLACKLIST: # e.g. /media/ or /pylucid_admin/ msg = ( "Sorry, page slug '/<strong>%s</strong>/' is not usable!" " (Not usable slugs are: %s)") % (self.slug, ", ".join( settings.SLUG_BLACKLIST)) message_dict["slug"] = (mark_safe(msg), ) queryset = PageTree.on_site.filter(slug=self.slug, parent=self.parent) # Exclude the current object from the query if we are editing an # instance (as opposed to creating a new one) if self.pk is not None: queryset = queryset.exclude(pk=self.pk) exists = queryset.count() if exists: if self.parent == None: # parent is the tree root parent_url = "/" else: parent_url = self.parent.get_absolute_url() msg = "Page '%s<strong>%s</strong>/' exists already." % ( parent_url, self.slug) message_dict["slug"] = (mark_safe(msg), ) # Check if parent page is a ContentPage, a plugin page can't have any sub pages! if "parent" not in exclude and self.parent is not None and self.parent.page_type != self.PAGE_TYPE: parent_url = self.parent.get_absolute_url() msg = _( "Can't use the <strong>plugin</strong> page '%s' as parent page!" " Please choose a <strong>content</strong> page.") % parent_url message_dict["parent"] = (mark_safe(msg), ) # Prevents that a unprotected page created below a protected page. # TODO: Check this in unittests # validate_permit_group() method inherited from PermissionsBase self.validate_permit_group("permitViewGroup", exclude, message_dict) self.validate_permit_group("permitEditGroup", exclude, message_dict) # Warn user if PageTree permissions mismatch with sub pages # TODO: Check this in unittests queryset = PageTree.objects.filter(parent=self) self.check_sub_page_permissions( # method inherited from PermissionsBase ("permitViewGroup", "permitEditGroup"), exclude, message_dict, queryset) if message_dict: raise ValidationError(message_dict) def recusive_attribute(self, attribute): """ Goes the pagetree back to root and return the first match of attribute if not None. used e.g. with permitViewGroup and permitEditGroup from self.validate_permit_group() and self.check_sub_page_permissions() """ parent = self.parent if parent is None: # parent is the tree root return None if getattr(parent, attribute) is not None: # the attribute was set by parent page return parent else: # go down to root return parent.recusive_attribute(attribute) _url_cache = LocalSyncCache(id="PageTree_absolute_url") def get_absolute_url(self): """ absolute url *without* language code (without domain/host part) """ try: url = self._url_cache[self.pk] # print "PageTree url cache len: %s, pk: %s" % (len(self._url_cache), self.pk) except KeyError: if self.parent: parent_shortcut = self.parent.get_absolute_url() url = parent_shortcut + self.slug + "/" else: url = "/" + self.slug + "/" self._url_cache[self.pk] = url return url def get_absolute_uri(self): """ absolute url with domain/host part (but without language code) """ absolute_url = self.get_absolute_url() domain = self.site.domain return "http://" + domain + absolute_url def save(self, *args, **kwargs): """ reset PageMeta and PageTree url cache """ from pagemeta import PageMeta # against import loops. # Clean the local url cache dict self._url_cache.clear() PageMeta._url_cache.clear() # FIXME: We must only update the cache for the current SITE not for all sites. try: cache.smooth_update( ) # Save "last change" timestamp in django-tools SmoothCacheBackend except AttributeError: # No SmoothCacheBackend used -> clean the complete cache cache.clear() return super(PageTree, self).save(*args, **kwargs) def get_site(self): """ used e.g. for self.get_absolute_uri() and the admin page """ return self.site def __unicode__(self): return u"PageTree %r (id: %i, site: %s, type: %s)" % ( self.slug, self.id, self.site.domain, self.TYPE_DICT.get(self.page_type)) class Meta: app_label = 'pylucid' verbose_name_plural = verbose_name = "PageTree" unique_together = (("site", "slug", "parent"), ) # FIXME: It would be great if we can order by get_absolute_url() # ordering = ("site", "id", "position") ordering = ("-lastupdatetime", )
from django.utils.importlib import import_module from django.utils.log import getLogger from django.views.decorators.csrf import csrf_protect from django_tools.local_sync_cache.local_sync_cache import LocalSyncCache from pylucid_project.utils.python_tools import has_init_file from pylucid_project.utils.url_debug import log_urls, debug_log_urls # see: http://www.pylucid.org/permalink/443/how-to-display-debug-information log = getLogger("pylucid.pylucid_plugins") # PYLUCID_PLUGINS = None _PLUGIN_OBJ_CACHE = {} # cache for PyLucidPlugin.get_plugin_object() _PLUGIN_URL_CACHE = LocalSyncCache( id="plugin_url_cache") # cache for PyLucidPlugin.get_prefix_urlpatterns() class PluginNotOnSite(Exception): """ PluginPage doesn't exist on current page. """ pass # Use django-tools local sync cache for sync the plugin urls in # a multi-threaded environment. PLUGIN_URLS_SYNC_DICT = LocalSyncCache(id="plugin url patterns") class PluginURLPattern(RegexURLResolver): """ Handle all url patterns from PyLucid plugins.