Changeset 1584

Show
Ignore:
Timestamp:
05/23/08 12:53:25 (6 months ago)
Author:
PerttuRantaaho
Message:

Refractored models.py to models package.
Moved delete_page_cache() from models.py to system.utils.
Dropped obsolete Preferences model. Updated unit tests and other code which still imported Prefereces.

Location:
trunk/pylucid
Files:
6 added
1 removed
11 modified
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/pylucid/PyLucid/db/page.py

    r1551 r1584  
    11#!/usr/bin/python 
    2 # -*- coding: UTF-8 -*- 
     2# -*- coding: utf-8 -*- 
    33""" 
    44    PyLucid.db.page 
     
    2525from django.utils.translation import ugettext as _ 
    2626 
    27 from PyLucid.models import User, Page 
     27from PyLucid.models import Page 
    2828from PyLucid.tools.tree_generator import TreeGenerator 
    2929 
  • trunk/pylucid/PyLucid/install/low_level_admin.py

    r1523 r1584  
    1010 
    1111from django.conf import settings 
    12 from PyLucid.models import Preference, Page, Template 
     12from PyLucid.models import Page, Template 
    1313from PyLucid.install.BaseInstall import BaseInstall 
    1414 
  • trunk/pylucid/PyLucid/install/update.py

    r1464 r1584  
    1414from PyLucid.install.BaseInstall import BaseInstall 
    1515from PyLucid.settings import OLD_TABLE_PREFIX 
    16 from PyLucid.models import JS_LoginData, Page, Template, Style, Preference 
     16from PyLucid.models import JS_LoginData, Page, Template, Style 
    1717 
    1818from django.contrib.auth.models import User 
  • trunk/pylucid/PyLucid/models/Page.py

    r1580 r1584  
    11# -*- coding: utf-8 -*- 
    2  
    32""" 
    4     PyLucid.models 
    5     ~~~~~~~~~~~~~~ 
    6  
    7     The database models for PyLucid 
    8     based on Django's ORM. 
     3    PyLucid.models.Page 
     4    ~~~~~~~~~~~~~~~~~~~ 
     5 
     6    Models for Page and PageArchive 
    97 
    108    Last commit info: 
     
    1816""" 
    1917 
    20 import os, posixpath, pickle 
    21 from pprint import pformat 
    22  
     18 
     19from django.db import models 
    2320from django.conf import settings 
    24 from django.db import models 
    2521from django.core.cache import cache 
    26 from django.contrib.auth.models import User, Group, UNUSABLE_PASSWORD 
    2722from django.utils.translation import ugettext as _ 
    28  
    29 from PyLucid.tools.newforms_utils import get_init_dict, setup_help_text 
    30 from PyLucid.tools.data_eval import data_eval, DataEvalError 
     23from django.contrib.auth.models import User, Group 
     24 
     25from PyLucid.system.utils import get_uri_base 
    3126from PyLucid.tools.shortcuts import getUniqueShortcut 
    32 from PyLucid.tools import crypt 
    33 from PyLucid.system.utils import get_uri_base 
    34 from PyLucid.system.plugin_import import get_plugin_config, get_plugin_version 
    35  
    36 from PyLucid.managers import PageManager 
    37 #from PyLucid.db.cache import delete_page_cache 
     27from PyLucid.system.exceptions import AccessDenied, LowLevelError 
    3828 
    3929 
     
    4838) 
    4939 
    50 def delete_page_cache(): 
     40 
     41class PageManager(models.Manager): 
    5142    """ 
    52     Delete all pages in the cache. 
    53     Needed, if: 
    54         - A template has been edited 
    55         - The menu changes (edit the page name, position, parent link) 
    56     TODO: move this function from models.py into a other nice place... 
     43    Manager for Page model. 
    5744    """ 
    58     for items in Page.objects.values('shortcut').iterator(): 
    59         shortcut = items["shortcut"] 
    60         cache_key = settings.PAGE_CACHE_PREFIX + shortcut 
    61         cache.delete(cache_key) 
    62  
     45 
     46    class WrongShortcut(LookupError): 
     47        """ URL string contained invalid shortcuts at the end. """ 
     48        pass 
     49     
     50    @property 
     51    def default_page(self): 
     52        """ Return default "index" page """  
     53        from PyLucid.models import Plugin 
     54        try: 
     55            preferences = Plugin.objects.get_preferences("system_settings") 
     56            page_id = preferences["index_page"] 
     57            return self.get(id__exact=page_id) 
     58        except Exception, e: 
     59            # The defaultPage-ID from the Preferences is wrong! Return first 
     60            # page if anything exists. 
     61            try: 
     62                return self.all().order_by("parent", "position")[0] 
     63            except IndexError, e: 
     64                msg = _("Error getting a cms page.") 
     65                raise LowLevelError(msg, e) 
     66 
     67    def _check_permission_tree(self,page,test_fcn): 
     68        """ 
     69        Check permissions of page and its parents. The second parameter 
     70        test_fcn(page_object) is the test function which must return true or false 
     71        corresponding to permssion to view specific page. 
     72        """ 
     73        while page: 
     74            if not test_fcn(page): 
     75                return False 
     76            page = page.parent 
     77        return True 
     78 
     79    def __check_publicView(self, page): 
     80        """ 
     81        Return true/false if page permits anonymous view. 
     82        """ 
     83        return page.permitViewPublic 
     84 
     85    def __check_groupView(self, user_group_ids): 
     86        """ 
     87        Return true/false if page permits anonymous view. 
     88        """ 
     89        def check(page): 
     90            if page.permitViewGroup == None: 
     91                return True 
     92            else: 
     93                return page.permitViewGroup.id in user_group_ids 
     94        return check 
     95     
     96 
     97    def get_by_shortcut(self,url_shortcuts,user): 
     98        """ 
     99        Returns a page object matching the shortcut.  
     100 
     101        PyLucid urls are build from the page shortcuts: 
     102        domain.tld/shortcut1/shortcut2/. Only the last existing shortcut will 
     103        be used. All other parts of the url are simply ignored. 
     104 
     105        If url_shortcuts contains no shortcut -> Return the default page (stored in the 
     106        Preference table). 
     107        If a part of the url is wrong -> Raise WrongShortcut, with correct path in message 
     108        If a anonymous user would get a permitViewPublic page -> Raise AccessDenied. 
     109        If no matching page is found -> Raise Page.DoesNotExist 
     110 
     111        TODO: Support user group based access control. 
     112        """ 
     113 
     114        # /shortcut1/shortcut2/ -> ['shortcut1','shortcut2'] 
     115        shortcuts = url_shortcuts.strip("/").split('/') 
     116 
     117        if shortcuts[0] == "": 
     118            # No shortcuts return default_page 
     119            return self.default_page 
     120         
     121        # Check shortcuts in reversed order 
     122        shortcuts.reverse() 
     123        wrong_shortcut = False 
     124        if not user.is_anonymous(): 
     125            user_groups = [x['id'] for x in user.groups.all().values('id')] 
     126        for shortcut in shortcuts: 
     127            try: 
     128                page = self.select_related().get(shortcut__exact=shortcut) 
     129            except self.model.DoesNotExist: 
     130                wrong_shortcut = True 
     131                continue             
     132            if user.is_anonymous(): 
     133                if not self._check_permission_tree(page,self.__check_publicView): 
     134                    # the page or its parent is not viewable for anonymous user 
     135                    raise AccessDenied 
     136            elif not user.is_superuser: 
     137                # Superuser can see all pages. 
     138                if not self._check_permission_tree(page,self.__check_groupView(user_groups)): 
     139                    # User is logged in. Check if page is restricted to user group 
     140                    raise AccessDenied 
     141 
     142            # Found an existing, viewable page 
     143            if wrong_shortcut: 
     144                # At least one of the shortcuts in the url was wrong -> raise 
     145                raise self.WrongShortcut, page.get_absolute_url() 
     146            return page 
     147        # No right page found 
     148        raise self.model.DoesNotExist 
     149 
     150#______________________________________________________________________________ 
    63151 
    64152class Page(models.Model): 
     
    147235        help_text="Usergroup how can edit this page." 
    148236    ) 
     237 
     238    class Meta: 
     239        db_table = 'PyLucid_page' 
     240        app_label = 'PyLucid' 
    149241 
    150242    class Admin: 
     
    184276        The default page must have some settings. 
    185277        """ 
     278        from PyLucid.models import Plugin 
    186279        try: 
    187280            preferences = Plugin.objects.get_preferences("system_settings") 
     
    233326        -make shortcut unique 
    234327        """ 
     328        from PyLucid.models import Plugin 
    235329        try: 
    236330            preferences = Plugin.objects.get_preferences("system_settings") 
     
    261355        before save: check some data consistency to prevents inconsistent data. 
    262356        """ 
     357        from PyLucid.system.utils import delete_page_cache 
    263358        if self.id != None: 
    264359            # a existing page should be updated (It's not a new page ;) 
     
    286381    def delete(self): 
    287382        # Delete all pages in the cache. 
     383        from PyLucid.system.utils import delete_page_cache 
    288384        delete_page_cache() 
    289385        super(Page, self).delete() 
     
    429525    ) 
    430526 
     527    class Meta: 
     528        db_table = 'PyLucid_pagearchiv' 
     529        app_label = 'PyLucid' 
     530 
    431531    class Admin: 
    432532        list_display = ( 
     
    434534            "description", "lastupdatetime", "lastupdateby" 
    435535        ) 
    436  
    437 #______________________________________________________________________________ 
    438  
    439  
    440 class JS_LoginData(models.Model): 
    441     """ 
    442     This model class stores the needed SHA information for the PyLucid 
    443     JS-SHA-Login. 
    444     Note: We make a Monkey-Patch (?) and change the method set_password() from 
    445     the model class django.contrib.auth.models.User 
    446     """ 
    447     user = models.ForeignKey(User) 
    448  
    449     sha_checksum = models.CharField(max_length=192) 
    450     salt = models.CharField(max_length=5) 
    451  
    452     createtime = models.DateTimeField(auto_now_add=True) 
    453     lastupdatetime = models.DateTimeField(auto_now=True) 
    454  
    455     def set_unusable_password(self): 
    456         self.salt = UNUSABLE_PASSWORD 
    457         self.sha_checksum = UNUSABLE_PASSWORD 
    458  
    459     def set_password(self, raw_password): 
    460         raw_password = str(raw_password) 
    461         salt, sha_checksum = crypt.make_sha_checksum2(raw_password) 
    462         self.salt = salt 
    463         self.sha_checksum = sha_checksum 
    464  
    465     def __unicode__(self): 
    466         return self.user.username 
    467  
    468     class Admin: 
    469         list_display = ( 
    470             'user', 'sha_checksum', 'salt', 'createtime', 'lastupdatetime' 
    471         ) 
    472  
    473     class Meta: 
    474         verbose_name = verbose_name_plural = 'JS-LoginData' 
    475  
    476  
    477  
    478 # Save the original method 
    479 old_set_password = User.set_password 
    480  
    481 def set_password(user, raw_password): 
    482 #    print "set_password() debug:", user, raw_password 
    483     if user.id == None: 
    484         # It is a new user. We must save the django user accound first to get a 
    485         # existing user object with a ID and then the JS_LoginData can assign to it. 
    486         user.save() 
    487  
    488     # Save the password for the JS-SHA-Login: 
    489     login_data, status = JS_LoginData.objects.get_or_create(user = user) 
    490     login_data.set_password(raw_password) 
    491     login_data.save() 
    492  
    493     # Use the original method to set the django User password: 
    494     old_set_password(user, raw_password) 
    495  
    496  
    497 # Make a hook into Django's default way to set a new User Password. 
    498 # Get the new raw_password and set the PyLucid password, too. 
    499 User.set_password = set_password 
    500  
    501 #____________________________________________________________________ 
    502  
    503  
    504 class Preference(models.Model): 
    505     # Obsolete! 
    506     pass 
    507  
    508 #____________________________________________________________________ 
    509  
    510 preference_cache = {} 
    511  
    512 class PluginManager(models.Manager): 
    513     def get_preferences(self, plugin_name): 
    514         """ 
    515         returns the preference data dict, use the cache 
    516         """ 
    517         # Get the name of the plugin, if __file__ used 
    518         plugin_name = os.path.splitext(os.path.basename(plugin_name))[0] 
    519         #print "plugin name: '%s'" % plugin_name 
    520  
    521         if plugin_name in preference_cache: 
    522             return preference_cache[plugin_name] 
    523  
    524         plugin = self.get(plugin_name = plugin_name) 
    525         return plugin.get_preferences() 
    526  
    527  
    528 class Plugin(models.Model): 
    529     objects = PluginManager() 
    530  
    531     plugin_name = models.CharField(max_length=90, unique=True) 
    532  
    533     package_name = models.CharField(max_length=255) 
    534     author = models.CharField(blank=True, max_length=150) 
    535     url = models.CharField(blank=True, max_length=255) 
    536     description = models.CharField(blank=True, max_length=255) 
    537  
    538     pref_data_string = models.TextField( 
    539         null=True, blank=True, 
    540         help_text="printable representation of the newform data dictionary" 
    541     ) 
    542     can_deinstall = models.BooleanField(default=True, 
    543         help_text=( 
    544             "If false and/or not set:" 
    545             " This essential plugin can't be deinstalled." 
    546         ) 
    547     ) 
    548     active = models.BooleanField(default=False, 
    549         help_text="Is this plugin is enabled and useable?" 
    550     ) 
    551  
    552     #__________________________________________________________________________ 
    553     # spezial methods 
    554  
    555     def init_pref_form(self, pref_form): 
    556         """ 
    557         Set self.pref_data_string from the given newforms form and his initial 
    558         values. 
    559         """ 
    560         init_dict = get_init_dict(pref_form) 
    561         preference_cache[self.plugin_name] = init_dict 
    562         self.set_pref_data_string(init_dict) 
    563  
    564     #__________________________________________________________________________ 
    565     # spezial set methods 
    566  
    567     def set_pref_data_string(self, data_dict): 
    568         """ 
    569         set the dict via pformat 
    570         """ 
    571         preference_cache[self.plugin_name] = data_dict 
    572         self.pref_data_string = pformat(data_dict) 
    573  
    574     #__________________________________________________________________________ 
    575     # spezial get methods 
    576  
    577     def get_preferences(self): 
    578         """ 
    579         evaluate the pformat string into a dict and return it. 
    580         """ 
    581         data_dict = data_eval(self.pref_data_string) 
    582         preference_cache[self.plugin_name] = data_dict 
    583         return data_dict 
    584  
    585     def get_pref_form(self, debug): 
    586         """ 
    587         Get the 'PreferencesForm' newform class from the plugin modul, insert 
    588         initial information into the help text and return the form. 
    589         """ 
    590         plugin_config = get_plugin_config( 
    591             self.package_name, self.plugin_name, debug 
    592         ) 
    593         form = getattr(plugin_config, "PreferencesForm") 
    594         setup_help_text(form) 
    595         return form 
    596  
    597     def get_version_string(self, debug=False): 
    598         """ 
    599         Returned the version string from the plugin module 
    600         """ 
    601         return get_plugin_version(self.package_name, self.plugin_name, debug) 
    602  
    603     #-------------------------------------------------------------------------- 
    604  
    605     def save(self): 
    606         """ 
    607         Save a new plugin or update changed data. 
    608         before save: check some data consistency to prevents inconsistent data. 
    609         """ 
    610         if self.can_deinstall==False and self.active==False: 
    611             # This plugin can't be deactivaded! 
    612             # If reinit misses, the plugin is deinstalled. After a install with 
    613             # the plugin admin, normaly the plugin would not be acivated 
    614             # automaticly. So we activated it here: 
    615             self.active = True 
    616  
    617         super(Plugin, self).save() # Call the "real" save() method 
    618  
    619     class Meta: 
    620         permissions = ( 
    621             # Permission identifier     human-readable permission name 
    622             ("can_use",                 "Can use the plugin"), 
    623         ) 
    624  
    625     class Admin: 
    626         list_display = ( 
    627             "active", "plugin_name", "description", "can_deinstall" 
    628         ) 
    629         list_display_links = ("plugin_name",) 
    630         ordering = ('package_name', 'plugin_name') 
    631         list_filter = ("author","package_name", "can_deinstall") 
    632  
    633     def __unicode__(self): 
    634         txt = u"%s - %s" % (self.package_name, self.plugin_name) 
    635         return txt.replace(u"_",u" ") 
    636  
    637  
    638 #______________________________________________________________________________ 
    639  
    640  
    641 class Style(models.Model): 
    642     """ 
    643     The global stylesheet. 
    644     On save() we try to store the CSS content into a local cache file in the 
    645     media path. This only works, if the process has the writeability. 
    646     In a simple shared web hosting environment, the http server runs the web 
    647     app with the user 'nobody', so he has not the writeability. In this case, 
    648     the stylesheet must be request via a _command url. 
    649     Important: The method get_absolute_url() doesn't check if the local cache 
    650     file was written succsessfully in the past! This it the job for the 
    651     page_style plugin. The method returns allways the url to the locale cache 
    652     file. 
    653     """ 
    654     name = models.CharField(unique=True, max_length=150) 
    655  
    656     createtime = models.DateTimeField(auto_now_add=True) 
    657     lastupdatetime = models.DateTimeField(auto_now=True) 
    658  
    659     createby = models.ForeignKey(User, related_name="style_createby", 
    660         null=True, blank=True 
    661     ) 
    662     lastupdateby = models.ForeignKey(User, related_name="style_lastupdateby", 
    663         null=True, blank=True 
    664     ) 
    665  
    666     description = models.TextField(null=True, blank=True) 
    667     content = models.TextField() 
    668  
    669     class Admin: 
    670         list_display = ( 
    671             "id", "name", "description", "createtime", "lastupdatetime" 
    672         ) 
    673         list_display_links = ("name",) 
    674  
    675     def __unicode__(self): 
    676         return self.name 
    677  
    678     def get_filename(self): 
    679         """ 
    680         How to make it URL and filesystem save? 
    681         """ 
    682         return self.name + ".css" 
    683  
    684     def get_absolute_url(self): 
    685         """ 
    686         Get the absolute url (without the domain/host part) for the stylesheet 
    687         file stored in the media path. 
    688         Important: Returns allways a link to the locale cache file, it doesn't 
    689         check if the file exists! 
    690         """ 
    691         filename = self.get_filename() 
    692         url = posixpath.join( 
    693             "", 
    694             settings.MEDIA_URL, 
    695             settings.PYLUCID_MEDIA_DIR, 
    696             filename, # FIXME: url save? 
    697         ) 
    698         return url 
    699  
    700     def get_filepath(self): 
    701         """ 
    702         Get the file path to the local stylesheet file. 
    703         Important: It is not tested if the file exists! 
    704         FIXME: How to handle special characters? 
    705         """ 
    706         filename = self.get_filename() 
    707         filepath = os.path.join( 
    708             settings.MEDIA_ROOT, 
    709             settings.PYLUCID_MEDIA_DIR, 
    710             filename 
    711         ) 
    712         # FIXME: Should we use os.path.abspath() ? 
    713         return filepath 
    714  
    715     def save(self): 
    716         """ 
    717         Save a new or updated stylesheet. 
    718         try to store the content into the cache file in the media path. 
    719         """ 
    720         filepath = self.get_filepath() 
    721         try: 
    722             f = file(filepath, "w") # FIXME: Encoding? 
    723             content = self.content.encode(settings.FILE_CHARSET) 
    724             f.write(content) 
    725             f.close() 
    726         except Exception, e: 
    727             # FIXME: How can we give feedback? 
    728 #            print "Style save error:", e # Olny for dev server 
    729             pass 
    730  
    731         #Delete the page cache if a stylesheet was edited. 
    732         delete_page_cache() 
    733  
    734         super(Style, self).save() # Call the "real" save() method 
    735  
    736  
    737 class Template(models.Model): 
    738     name = models.CharField(unique=True, max_length=150) 
    739  
    740     createtime = models.DateTimeField(auto_now_add=True) 
    741     lastupdatetime = models.DateTimeField(auto_now=True) 
    742  
    743     createby = models.ForeignKey(User, related_name="template_createby", 
    744         null=True, blank=True 
    745     ) 
    746     lastupdateby = models.ForeignKey(User, related_name="template_lastupdateby", 
    747         null=True, blank=True 
    748     ) 
    749  
    750     description = models.TextField() 
    751     content = models.TextField() 
    752  
    753     def save(self): 
    754         """ 
    755         Delete the page cache if a template was edited. 
    756         """ 
    757         delete_page_cache() 
    758  
    759         super(Template, self).save() # Call the "real" save() method 
    760  
    761     class Admin: 
    762         list_display = ("id", "name", "description") 
    763         list_display_links = ("name",) 
    764  
    765     def __unicode__(self): 
    766         return self.name 
  • trunk/pylucid/PyLucid/plugins_internal/back_links/back_links.py

    r1364 r1584  
    11#!/usr/bin/python 
    2 # -*- coding: UTF-8 -*- 
     2# -*- coding: utf-8 -*- 
    33 
    44""" 
     
    3636 
    3737from PyLucid.system.BasePlugin import PyLucidBasePlugin 
    38 from PyLucid.models import Page, Preference 
     38from PyLucid.models import Page 
    3939 
    4040class back_links(PyLucidBasePlugin): 
  • trunk/pylucid/PyLucid/system/utils.py

    r1409 r1584  
    2020 
    2121from django.conf import settings 
     22from django.core.cache import cache 
     23 
     24def delete_page_cache(): 
     25    """ 
     26    Delete all pages in the cache. 
     27    Needed, if: 
     28        - A template has been edited 
     29        - The menu changes (edit the page name, position, parent link) 
     30    TODO: move this function from models.py into a other nice place... 
     31    """ 
     32    from PyLucid.models import Page 
     33    for items in Page.objects.values('shortcut').iterator(): 
     34        shortcut = items["shortcut"] 
     35        cache_key = settings.PAGE_CACHE_PREFIX + shortcut 
     36        cache.delete(cache_key) 
    2237 
    2338def setup_debug(request): 
  • trunk/pylucid/tests/plugin_backlinks.py

    r1510 r1584  
    2828from django.conf import settings 
    2929 
    30 from PyLucid.models import Page, Template, Preference 
     30from PyLucid.models import Page, Template 
    3131 
    3232 
  • trunk/pylucid/tests/plugin_main_menu.py

    r1510 r1584  
    2323from django.conf import settings 
    2424 
    25 from PyLucid.models import Page, Template, Preference 
     25from PyLucid.models import Page, Template 
    2626 
    2727 
  • trunk/pylucid/tests/preferences.py

    r1583 r1584  
    2121 
    2222import tests 
    23 from PyLucid.models import Preference, Plugin 
     23from PyLucid.models import Plugin 
    2424 
    2525 
  • trunk/pylucid/tests/sha1_js_login.py

    r1507 r1584  
    2525from PyLucid import models, settings 
    2626from PyLucid.plugins_internal.auth.auth import auth 
    27 from PyLucid.models import User, JS_LoginData 
     27from PyLucid.models.JS_LoginData import User, JS_LoginData 
    2828from PyLucid.install.install import _create_or_update_superuser 
    2929from PyLucid.tools import crypt 
  • trunk/pylucid/tests/test_plugin_manager.py

    <
    r1507 r1584  
    2525from django.conf import settings 
    2626 
    27 from PyLucid.models import User