alex@706: # -*- coding: iso-8859-1 -*- alex@706: """ alex@706: MoinMoin - Multiple configuration handler and Configuration defaults class alex@706: alex@706: @copyright: 2000-2004 by Jrgen Hermann alex@706: @license: GNU GPL, see COPYING for details. alex@706: """ alex@706: alex@706: import re, os, sys alex@706: from MoinMoin import error alex@706: import MoinMoin.auth as authmodule alex@706: alex@706: _url_re_cache = None alex@706: _farmconfig_mtime = None alex@706: _config_cache = {} alex@706: alex@706: alex@706: def _importConfigModule(name): alex@706: """ Import and return configuration module and its modification time alex@706: alex@706: Handle all errors except ImportError, because missing file is not alex@706: always an error. alex@706: alex@706: @param name: module name alex@706: @rtype: tuple alex@706: @return: module, modification time alex@706: """ alex@706: try: alex@706: module = __import__(name, globals(), {}) alex@706: mtime = os.path.getmtime(module.__file__) alex@706: except ImportError: alex@706: raise alex@706: except IndentationError, err: alex@706: msg = 'IndentationError: %s\n' % str(err) + ''' alex@706: alex@706: The configuration files are python modules. Therefore, whitespace is alex@706: important. Make sure that you use only spaces, no tabs are allowed here! alex@706: You have to use four spaces at the beginning of the line mostly. alex@706: ''' alex@706: raise error.ConfigurationError(msg) alex@706: except Exception, err: alex@706: msg = '%s: %s' % (err.__class__.__name__, str(err)) alex@706: raise error.ConfigurationError(msg) alex@706: return module, mtime alex@706: alex@706: alex@706: def _url_re_list(): alex@706: """ Return url matching regular expression alex@706: alex@706: Import wikis list from farmconfig on the first call and compile the alex@706: regexes. Later then return the cached regex list. alex@706: alex@706: @rtype: list of tuples of (name, compiled re object) alex@706: @return: url to wiki config name matching list alex@706: """ alex@706: global _url_re_cache, _farmconfig_mtime alex@706: if _url_re_cache is None: alex@706: try: alex@706: farmconfig, _farmconfig_mtime = _importConfigModule('farmconfig') alex@706: except ImportError: alex@706: # Default to wikiconfig for all urls. alex@706: _farmconfig_mtime = 0 alex@706: _url_re_cache = [('wikiconfig', re.compile(r'.')), ] # matches everything alex@706: else: alex@706: try: alex@706: cache = [] alex@706: for name, regex in farmconfig.wikis: alex@706: cache.append((name, re.compile(regex))) alex@706: _url_re_cache = cache alex@706: except AttributeError: alex@706: msg = """ alex@706: Missing required 'wikis' list in 'farmconfig.py'. alex@706: alex@706: If you run a single wiki you do not need farmconfig.py. Delete it and alex@706: use wikiconfig.py. alex@706: """ alex@706: raise error.ConfigurationError(msg) alex@706: return _url_re_cache alex@706: alex@706: alex@706: def _makeConfig(name): alex@706: """ Create and return a config instance alex@706: alex@706: Timestamp config with either module mtime or farmconfig mtime. This alex@706: mtime can be used later to invalidate older caches. alex@706: alex@706: @param name: module name alex@706: @rtype: DefaultConfig sub class instance alex@706: @return: new configuration instance alex@706: """ alex@706: global _farmconfig_mtime alex@706: try: alex@706: module, mtime = _importConfigModule(name) alex@706: configClass = getattr(module, 'Config') alex@706: cfg = configClass(name) alex@706: cfg.cfg_mtime = max(mtime, _farmconfig_mtime) alex@706: except ImportError, err: alex@706: msg = 'ImportError: %s\n' % str(err) + ''' alex@706: alex@706: Check that the file is in the same directory as the server script. If alex@706: it is not, you must add the path of the directory where the file is alex@706: located to the python path in the server script. See the comments at alex@706: the top of the server script. alex@706: alex@706: Check that the configuration file name is either "wikiconfig.py" or the alex@706: module name specified in the wikis list in farmconfig.py. Note that the alex@706: module name does not include the ".py" suffix. alex@706: ''' alex@706: raise error.ConfigurationError(msg) alex@706: except AttributeError: alex@706: msg = ''' alex@706: Could not find required "Config" class in "%(name)s.py". This might alex@706: happen if you are trying to use a pre 1.3 configuration file, or made a alex@706: syntax or spelling error. alex@706: alex@706: Please check your configuration file. As an example for correct syntax, alex@706: use the wikiconfig.py file from the distribution. alex@706: ''' % {'name': name} alex@706: raise error.ConfigurationError(msg) alex@706: return cfg alex@706: alex@706: alex@706: def _getConfigName(url): alex@706: """ Return config name for url or raise """ alex@706: for name, regex in _url_re_list(): alex@706: match = regex.match(url) alex@706: if match: alex@706: return name alex@706: # nothing matched alex@706: msg = ''' alex@706: Could not find a match for url: "%(url)s". alex@706: alex@706: Check your URL regular expressions in the "wikis" list in alex@706: "farmconfig.py". alex@706: ''' % {'url': url} alex@706: raise error.ConfigurationError(msg) alex@706: alex@706: alex@706: def getConfig(url): alex@706: """ Return cached config instance for url or create new one alex@706: alex@706: If called by many threads in the same time multiple config alex@706: instances might be created. The first created item will be alex@706: returned, using dict.setdefault. alex@706: alex@706: @param url: the url from request, possibly matching specific wiki alex@706: @rtype: DefaultConfig subclass instance alex@706: @return: config object for specific wiki alex@706: """ alex@706: configName = _getConfigName(url) alex@706: try: alex@706: config = _config_cache[configName] alex@706: except KeyError: alex@706: config = _makeConfig(configName) alex@706: config = _config_cache.setdefault(configName, config) alex@706: return config alex@706: alex@706: alex@706: # This is a way to mark some text for the gettext tools so that they don't alex@706: # get orphaned. See http://www.python.org/doc/current/lib/node278.html. alex@706: def _(text): return text alex@706: alex@706: alex@706: class DefaultConfig: alex@706: """ default config values """ alex@706: alex@706: # All acl_right lines must use unicode! alex@706: acl_rights_default = u"Trusted:read,write,delete,revert Known:read,write,delete,revert All:read,write" alex@706: acl_rights_before = u"" alex@706: acl_rights_after = u"" alex@706: acl_rights_valid = ['read', 'write', 'delete', 'revert', 'admin'] alex@706: alex@706: actions_excluded = [] # ['DeletePage', 'AttachFile', 'RenamePage', 'test', ] alex@706: allow_xslt = 0 alex@706: attachments = None # {'dir': path, 'url': url-prefix} alex@706: auth = [authmodule.moin_cookie] alex@706: alex@706: backup_compression = 'gz' alex@706: backup_users = [] alex@706: backup_include = [] alex@706: backup_exclude = [ alex@706: r"(.+\.py(c|o)$)", alex@706: r"%(cache_dir)s", alex@706: r"%(/)spages%(/)s.+%(/)scache%(/)s[^%(/)s]+$" % {'/': os.sep}, alex@706: r"%(/)s(edit-lock|event-log|\.DS_Store)$" % {'/': os.sep}, alex@706: ] alex@706: backup_storage_dir = '/tmp' alex@706: backup_restore_target_dir = '/tmp' alex@706: alex@706: bang_meta = 1 alex@706: caching_formats = ['text_html'] alex@706: changed_time_fmt = '%H:%M' alex@706: # chars_{upper,lower,digits,spaces} see MoinMoin/util/chartypes.py alex@706: # if you have gdchart, add something like alex@706: # chart_options = {'width = 720, 'height': 540} alex@706: chart_options = None alex@706: config_check_enabled = 0 alex@706: cookie_domain = None # use '.domain.tld" for a farm with hosts in that domain alex@706: cookie_path = None # use '/wikifarm" for a farm with pathes below that path alex@706: cookie_lifetime = 12 # 12 hours from now alex@706: data_dir = './data/' alex@706: data_underlay_dir = './underlay/' alex@706: date_fmt = '%Y-%m-%d' alex@706: datetime_fmt = '%Y-%m-%d %H:%M:%S' alex@706: default_markup = 'wiki' alex@706: docbook_html_dir = r"/usr/share/xml/docbook/stylesheet/nwalsh/html/" # correct for debian sarge alex@706: editor_default = 'text' # which editor is called when nothing is specified alex@706: editor_ui = 'freechoice' # which editor links are shown on user interface alex@706: editor_force = False alex@706: editor_quickhelp = { # editor markup hints quickhelp alex@706: 'wiki': _("""\ alex@706: Emphasis:: [[Verbatim('')]]''italics''[[Verbatim('')]]; [[Verbatim(''')]]'''bold'''[[Verbatim(''')]]; [[Verbatim(''''')]]'''''bold italics'''''[[Verbatim(''''')]]; [[Verbatim('')]]''mixed ''[[Verbatim(''')]]'''''bold'''[[Verbatim(''')]] and italics''[[Verbatim('')]]; [[Verbatim(----)]] horizontal rule. alex@706: Headings:: [[Verbatim(=)]] Title 1 [[Verbatim(=)]]; [[Verbatim(==)]] Title 2 [[Verbatim(==)]]; [[Verbatim(===)]] Title 3 [[Verbatim(===)]]; [[Verbatim(====)]] Title 4 [[Verbatim(====)]]; [[Verbatim(=====)]] Title 5 [[Verbatim(=====)]]. alex@706: Lists:: space and one of: * bullets; 1., a., A., i., I. numbered items; 1.#n start numbering at n; space alone indents. alex@706: Links:: [[Verbatim(JoinCapitalizedWords)]]; [[Verbatim(["brackets and double quotes"])]]; url; [url]; [url label]. alex@706: Tables:: || cell text |||| cell text spanning 2 columns ||; no trailing white space allowed after tables or titles. alex@706: alex@706: (!) For more help, see HelpOnEditing or SyntaxReference. alex@706: """), alex@706: 'rst': _("""\ alex@706: Emphasis: *italic* **bold** ``monospace``
alex@706:
alex@706: Headings: Heading 1  Heading 2  Heading 3
alex@706:           =========  ---------  ~~~~~~~~~
alex@706: 
alex@706: Horizontal rule: ---- 
alex@706: Links: TrailingUnderscore_ `multi word with backticks`_ external_ 
alex@706: 
alex@706: .. _external: http://external-site.net/foo/
alex@706: 
alex@706: Lists: * bullets; 1., a. numbered items.
alex@706: 
alex@706:
alex@706: (!) For more help, see the alex@706: alex@706: reStructuredText Quick Reference alex@706: . alex@706: """), alex@706: } alex@706: edit_locking = 'warn 10' # None, 'warn ', 'lock ' alex@706: edit_rows = 20 alex@706: alex@706: hacks = {} # { 'feature1': value1, ... } alex@706: # Configuration for features still in development. alex@706: # For boolean stuff just use config like this: alex@706: # hacks = { 'feature': True, ...} alex@706: # and in the code use: alex@706: # if cfg.hacks.get('feature', False): alex@706: # A non-existing hack key should ever mean False, None, "", [] or {}! alex@706: alex@706: hosts_deny = [] alex@706: html_head = '' alex@706: html_head_queries = '''\n''' alex@706: html_head_posts = '''\n''' alex@706: html_head_index = '''\n''' alex@706: html_head_normal = '''\n''' alex@706: html_pagetitle = None alex@706: alex@706: interwiki_preferred = [] # list of wiki names to show at top of interwiki list alex@706: alex@706: language_default = 'en' alex@706: language_ignore_browser = False # ignore browser settings, use language_default alex@706: # or user prefs alex@706: alex@706: lupy_search = False # disabled until lupy is finished alex@706: alex@706: mail_login = None # or "user pwd" if you need to use SMTP AUTH alex@706: mail_sendmail = None # "/usr/sbin/sendmail -t -i" to not use SMTP, but sendmail alex@706: mail_smarthost = None alex@718: mail_from = None # u'Juergen Wiki ' alex@718: alex@718: mail_import_subpage_template = u"$from-$date-$subject" # used for mail import alex@718: mail_import_wiki_address = None # the e-mail address for e-mails that should go into the wiki alex@718: mail_import_secret = "" alex@706: alex@706: navi_bar = [u'RecentChanges', u'FindPage', u'HelpContents', ] alex@706: nonexist_qm = 0 alex@706: alex@706: page_credits = [ alex@706: 'MoinMoin Powered', alex@706: 'Python Powered', alex@706: 'Valid HTML 4.01', alex@706: ] alex@706: page_footer1 = '' alex@706: page_footer2 = '' alex@706: alex@706: page_header1 = '' alex@706: page_header2 = '' alex@706: alex@706: page_front_page = u'HelpOnLanguages' # this will make people choose a sane config alex@706: page_local_spelling_words = u'LocalSpellingWords' alex@706: page_category_regex = u'^Category[A-Z]' alex@706: page_dict_regex = u'[a-z0-9]Dict$' alex@706: page_group_regex = u'[a-z0-9]Group$' alex@706: page_template_regex = u'[a-z0-9]Template$' alex@706: alex@706: page_license_enabled = 0 alex@706: page_license_page = u'WikiLicense' alex@706: alex@706: # These icons will show in this order in the iconbar, unless they alex@706: # are not relevant, e.g email icon when the wiki is not configured alex@706: # for email. alex@706: page_iconbar = ["up", "edit", "view", "diff", "info", "subscribe", "raw", "print",] alex@706: alex@706: # Standard buttons in the iconbar alex@706: page_icons_table = { alex@706: # key last part of url, title, icon-key alex@706: 'help': ("%(q_page_help_contents)s", "%(page_help_contents)s", "help"), alex@706: 'find': ("%(q_page_find_page)s?value=%(q_page_name)s", "%(page_find_page)s", "find"), alex@706: 'diff': ("%(q_page_name)s?action=diff", _("Diffs"), "diff"), alex@706: 'info': ("%(q_page_name)s?action=info", _("Info"), "info"), alex@706: 'edit': ("%(q_page_name)s?action=edit", _("Edit"), "edit"), alex@706: 'unsubscribe': ("%(q_page_name)s?action=subscribe", _("UnSubscribe"), "unsubscribe"), alex@706: 'subscribe': ("%(q_page_name)s?action=subscribe", _("Subscribe"), "subscribe"), alex@706: 'raw': ("%(q_page_name)s?action=raw", _("Raw"), "raw"), alex@706: 'xml': ("%(q_page_name)s?action=show&mimetype=text/xml", _("XML"), "xml"), alex@706: 'print': ("%(q_page_name)s?action=print", _("Print"), "print"), alex@706: 'view': ("%(q_page_name)s", _("View"), "view"), alex@706: 'up': ("%(q_page_parent_page)s", _("Up"), "up"), alex@706: } alex@706: refresh = None # (minimum_delay, type), e.g.: (2, 'internal') alex@706: rss_cache = 60 # suggested caching time for RecentChanges RSS, in seconds alex@706: shared_intermap = None # can be string or list of strings (filenames) alex@706: show_hosts = 1 alex@706: show_interwiki = 0 alex@706: show_login = 1 alex@706: show_names = True alex@706: show_section_numbers = 0 alex@706: show_timings = 0 alex@706: show_version = 0 alex@706: siteid = 'default' alex@706: stylesheets = [] # list of tuples (media, csshref) to insert after theme css, before user css alex@706: superuser = [] # list of unicode user names that have super powers :) alex@706: alex@706: surge_action_limits = { # allow max. requests per
secs alex@706: # action: (count, dt) alex@706: 'all': (30, 30), alex@706: 'show': (30, 60), alex@706: 'recall': (5, 60), alex@706: 'raw': (20, 40), # some people use this for css alex@706: 'AttachFile': (90, 60), alex@706: 'diff': (30, 60), alex@706: 'fullsearch': (5, 60), alex@706: 'edit': (10, 120), alex@706: 'rss_rc': (1, 60), alex@706: 'default': (30, 60), alex@706: } alex@706: surge_lockout_time = 3600 # secs you get locked out when you ignore warnings alex@706: alex@706: theme_default = 'modern' alex@706: theme_force = False alex@706: trail_size = 5 alex@706: tz_offset = 0.0 # default time zone offset in hours from UTC alex@706: user_autocreate = False # do we auto-create user profiles alex@706: user_email_unique = True # do we check whether a user's email is unique? alex@706: alex@706: # a regex of HTTP_USER_AGENTS that should be excluded from logging alex@706: # and receive a FORBIDDEN for anything except viewing a page alex@706: ua_spiders = ('archiver|cfetch|crawler|curl|gigabot|googlebot|holmes|htdig|httrack|httpunit|jeeves|larbin|leech|' alex@706: 'linkbot|linkmap|linkwalk|mercator|mirror|msnbot|nutbot|omniexplorer|puf|robot|scooter|' alex@706: 'sherlock|slurp|sitecheck|spider|teleport|voyager|webreaper|wget') alex@706: alex@706: # Wiki identity alex@706: sitename = u'Untitled Wiki' alex@706: url_prefix = '/wiki' alex@706: logo_string = None alex@706: interwikiname = None alex@706: alex@706: url_mappings = {} alex@706: alex@706: user_checkbox_fields = [ alex@706: ('mailto_author', lambda _: _('Publish my email (not my wiki homepage) in author info')), alex@706: ('edit_on_doubleclick', lambda _: _('Open editor on double click')), alex@706: ('remember_last_visit', lambda _: _('Jump to last visited page instead of frontpage')), alex@706: ('show_nonexist_qm', lambda _: _('Show question mark for non-existing pagelinks')), alex@706: ('show_page_trail', lambda _: _('Show page trail')), alex@706: ('show_toolbar', lambda _: _('Show icon toolbar')), alex@706: ('show_topbottom', lambda _: _('Show top/bottom links in headings')), alex@706: ('show_fancy_diff', lambda _: _('Show fancy diffs')), alex@706: ('wikiname_add_spaces', lambda _: _('Add spaces to displayed wiki names')), alex@706: ('remember_me', lambda _: _('Remember login information')), alex@706: ('want_trivial', lambda _: _('Subscribe to trivial changes')), alex@706: alex@706: ('disabled', lambda _: _('Disable this account forever')), alex@706: # if an account is disabled, it may be used for looking up alex@706: # id -> username for page info and recent changes, but it alex@706: # is not usable for the user any more: alex@706: ] alex@706: user_checkbox_defaults = {'mailto_author': 0, alex@706: 'edit_on_doubleclick': 0, alex@706: 'remember_last_visit': 0, alex@706: 'show_nonexist_qm': nonexist_qm, alex@706: 'show_page_trail': 1, alex@706: 'show_toolbar': 1, alex@706: 'show_topbottom': 0, alex@706: 'show_fancy_diff': 1, alex@706: 'wikiname_add_spaces': 0, alex@706: 'remember_me': 1, alex@706: 'want_trivial': 0, alex@706: } alex@706: # don't let the user change those alex@706: # user_checkbox_disable = ['disabled', 'want_trivial'] alex@706: user_checkbox_disable = [] alex@706: alex@706: # remove those checkboxes: alex@706: #user_checkbox_remove = ['edit_on_doubleclick', 'show_nonexist_qm', 'show_toolbar', 'show_topbottom', alex@706: # 'show_fancy_diff', 'wikiname_add_spaces', 'remember_me', 'disabled',] alex@706: user_checkbox_remove = [] alex@706: alex@706: user_form_fields = [ alex@706: ('name', _('Name'), "text", "36", _("(Use Firstname''''''Lastname)")), alex@706: ('aliasname', _('Alias-Name'), "text", "36", ''), alex@706: ('password', _('Password'), "password", "36", ''), alex@706: ('password2', _('Password repeat'), "password", "36", _('(Only when changing passwords)')), alex@706: ('email', _('Email'), "text", "36", ''), alex@706: ('css_url', _('User CSS URL'), "text", "40", _('(Leave it empty for disabling user CSS)')), alex@706: ('edit_rows', _('Editor size'), "text", "3", ''), alex@706: ##('theme', _('Preferred theme'), [self._theme_select()]) alex@706: ##('', _('Editor Preference'), [self._editor_default_select()]) alex@706: ##('', _('Editor shown on UI'), [self._editor_ui_select()]) alex@706: ##('', _('Time zone'), [self._tz_select()]) alex@706: ##('', _('Date format'), [self._dtfmt_select()]) alex@706: ##('', _('Preferred language'), [self._lang_select()]) alex@706: ] alex@706: user_form_defaults = { # key: default alex@706: 'name': '', alex@706: 'aliasname': '', alex@706: 'password': '', alex@706: 'password2': '', alex@706: 'email': '', alex@706: 'css_url': '', alex@706: 'edit_rows': "20", alex@706: } alex@706: # don't let the user change those, but show them: alex@706: #user_form_disable = ['name', 'aliasname', 'email',] alex@706: user_form_disable = [] alex@706: alex@706: # remove those completely: alex@706: #user_form_remove = ['password', 'password2', 'css_url', 'logout', 'create', 'account_sendmail',] alex@706: user_form_remove = [] alex@706: alex@706: # attributes we do NOT save to the userpref file alex@706: user_transient_fields = ['id', 'valid', 'may', 'auth_username', 'trusted', 'password', 'password2', 'auth_method', 'auth_attribs'] alex@706: alex@706: user_homewiki = 'Self' # interwiki name for where user homepages are located alex@706: alex@706: unzip_single_file_size = 2.0 * 1000**2 alex@706: unzip_attachments_space = 200.0 * 1000**2 alex@706: unzip_attachments_count = 51 # 1 zip file + 50 files contained in it alex@706: alex@706: xmlrpc_putpage_enabled = 0 # if 0, putpage will write to a test page only alex@706: xmlrpc_putpage_trusted_only = 1 # if 1, you will need to be http auth authenticated alex@706: alex@706: SecurityPolicy = None alex@706: alex@706: def __init__(self, siteid): alex@706: """ Init Config instance """ alex@706: self.siteid = siteid alex@706: if self.config_check_enabled: alex@706: self._config_check() alex@706: alex@706: # define directories alex@706: self.moinmoin_dir = os.path.abspath(os.path.dirname(__file__)) alex@706: data_dir = os.path.normpath(self.data_dir) alex@706: self.data_dir = data_dir alex@706: for dirname in ('user', 'cache', 'plugin'): alex@706: name = dirname + '_dir' alex@706: if not getattr(self, name, None): alex@706: setattr(self, name, os.path.join(data_dir, dirname)) alex@706: alex@706: # Try to decode certain names which allow unicode alex@706: self._decode() alex@706: alex@706: self._check_directories() alex@706: alex@706: if not isinstance(self.superuser, list): alex@706: msg = """The superuser setting in your wiki configuration is not a list alex@706: (e.g. ['Sample User', 'AnotherUser']). alex@706: Please change it in your wiki configuration and try again.""" alex@706: raise error.ConfigurationError(msg) alex@706: alex@706: self._loadPluginModule() alex@706: alex@706: # Preparse user dicts alex@706: self._fillDicts() alex@706: alex@706: # Normalize values alex@706: self.language_default = self.language_default.lower() alex@706: alex@706: # Use site name as default name-logo alex@706: if self.logo_string is None: alex@706: self.logo_string = self.sitename alex@706: alex@706: # Check for needed modules alex@706: alex@706: # FIXME: maybe we should do this check later, just before a alex@706: # chart is needed, maybe in the chart module, instead doing it alex@706: # for each request. But this require a large refactoring of alex@706: # current code. alex@706: if self.chart_options: alex@706: try: alex@706: import gdchart alex@706: except ImportError: alex@706: self.chart_options = None alex@706: alex@706: # post process alex@706: # we replace any string placeholders with config values alex@706: # e.g u'%(page_front_page)s' % self alex@706: self.navi_bar = [elem % self for elem in self.navi_bar] alex@706: self.backup_exclude = [elem % self for elem in self.backup_exclude] alex@706: alex@706: # list to cache lupy searcher objects alex@706: self.lupy_searchers = [] alex@706: alex@706: # check if mail is possible and set flag: alex@706: self.mail_enabled = (self.mail_smarthost is not None or self.mail_sendmail is not None) and self.mail_from alex@706: alex@706: def _config_check(self): alex@706: """ Check namespace and warn about unknown names alex@706: alex@706: Warn about names which are not used by DefaultConfig, except alex@706: modules, classes, _private or __magic__ names. alex@706: alex@706: This check is disabled by default, when enabled, it will show an alex@706: error message with unknown names. alex@706: """ alex@706: unknown = ['"%s"' % name for name in dir(self) alex@706: if not name.startswith('_') and alex@706: not DefaultConfig.__dict__.has_key(name) and alex@706: not isinstance(getattr(self, name), (type(sys), type(DefaultConfig)))] alex@706: if unknown: alex@706: msg = """ alex@706: Unknown configuration options: %s. alex@706: alex@706: For more information, visit HelpOnConfiguration. Please check your alex@706: configuration for typos before requesting support or reporting a bug. alex@706: """ % ', '.join(unknown) alex@706: raise error.ConfigurationError(msg) alex@706: alex@706: def _decode(self): alex@706: """ Try to decode certain names, ignore unicode values alex@706: alex@706: Try to decode str using utf-8. If the decode fail, raise FatalError. alex@706: alex@706: Certain config variables should contain unicode values, and alex@706: should be defined with u'text' syntax. Python decode these if alex@706: the file have a 'coding' line. alex@706: alex@706: This will allow utf-8 users to use simple strings using, without alex@706: using u'string'. Other users will have to use u'string' for alex@706: these names, because we don't know what is the charset of the alex@706: config files. alex@706: """ alex@706: charset = 'utf-8' alex@706: message = u''' alex@706: "%(name)s" configuration variable is a string, but should be alex@706: unicode. Use %(name)s = u"value" syntax for unicode variables. alex@706: alex@706: Also check your "-*- coding -*-" line at the top of your configuration alex@706: file. It should match the actual charset of the configuration file. alex@706: ''' alex@706: alex@706: decode_names = ( alex@706: 'sitename', 'logo_string', 'navi_bar', 'page_front_page', alex@706: 'page_category_regex', 'page_dict_regex', alex@706: 'page_group_regex', 'page_template_regex', 'page_license_page', alex@706: 'page_local_spelling_words', 'acl_rights_default', alex@706: 'acl_rights_before', 'acl_rights_after', 'mail_from' alex@706: ) alex@706: alex@706: for name in decode_names: alex@706: attr = getattr(self, name, None) alex@706: if attr: alex@706: # Try to decode strings alex@706: if isinstance(attr, str): alex@706: try: alex@706: setattr(self, name, unicode(attr, charset)) alex@706: except UnicodeError: alex@706: raise error.ConfigurationError(message % alex@706: {'name': name}) alex@706: # Look into lists and try to decode strings inside them alex@706: elif isinstance(attr, list): alex@706: for i in xrange(len(attr)): alex@706: item = attr[i] alex@706: if isinstance(item, str): alex@706: try: alex@706: attr[i] = unicode(item, charset) alex@706: except UnicodeError: alex@706: raise error.ConfigurationError(message % alex@706: {'name': name}) alex@706: alex@706: def _check_directories(self): alex@706: """ Make sure directories are accessible alex@706: alex@706: Both data and underlay should exists and allow read, write and alex@706: execute. alex@706: """ alex@706: mode = os.F_OK | os.R_OK | os.W_OK | os.X_OK alex@706: for attr in ('data_dir', 'data_underlay_dir'): alex@706: path = getattr(self, attr) alex@706: alex@706: # allow an empty underlay path or None alex@706: if attr == 'data_underlay_dir' and not path: alex@706: continue alex@706: alex@706: path_pages = os.path.join(path, "pages") alex@706: if not (os.path.isdir(path_pages) and os.access(path_pages, mode)): alex@706: msg = ''' alex@706: %(attr)s "%(path)s" does not exists, or has incorrect ownership or alex@706: permissions. alex@706: alex@706: Make sure the directory and the subdirectory pages are owned by the web alex@706: server and are readable, writable and executable by the web server user alex@706: and group. alex@706: alex@706: It is recommended to use absolute paths and not relative paths. Check alex@706: also the spelling of the directory name. alex@706: ''' % {'attr': attr, 'path': path,} alex@706: raise error.ConfigurationError(msg) alex@706: alex@706: def _loadPluginModule(self): alex@706: """ import plugin module under configname.plugin alex@706: alex@706: To be able to import plugin from arbitrary path, we have to load alex@706: the base package once using imp.load_module. Later, we can use alex@706: standard __import__ call to load plugins in this package. alex@706: alex@706: Since each wiki has unique plugins, we load the plugin package alex@706: under the wiki configuration module, named self.siteid. alex@706: """ alex@706: import sys, imp alex@706: alex@706: name = self.siteid + '.plugin' alex@706: try: alex@706: # Lock other threads while we check and import alex@706: imp.acquire_lock() alex@706: try: alex@706: # If the module is not loaded, try to load it alex@706: if not name in sys.modules: alex@706: # Find module on disk and try to load - slow! alex@706: plugin_parent_dir = os.path.abspath(os.path.join(self.plugin_dir, '..')) alex@706: fp, path, info = imp.find_module('plugin', [plugin_parent_dir]) alex@706: try: alex@706: # Load the module and set in sys.modules alex@706: module = imp.load_module(name, fp, path, info) alex@706: sys.modules[self.siteid].plugin = module alex@706: finally: alex@706: # Make sure fp is closed properly alex@706: if fp: alex@706: fp.close() alex@706: finally: alex@706: imp.release_lock() alex@706: except ImportError, err: alex@706: msg = ''' alex@706: Could not import plugin package "%(path)s/plugin" because of ImportError: alex@706: %(err)s. alex@706: alex@706: Make sure your data directory path is correct, check permissions, and alex@706: that the data/plugin directory has an __init__.py file. alex@706: ''' % {'path': self.data_dir, 'err': str(err)} alex@706: raise error.ConfigurationError(msg) alex@706: alex@706: def _fillDicts(self): alex@706: """ fill config dicts alex@706: alex@706: Fills in missing dict keys of derived user config by copying alex@706: them from this base class. alex@706: """ alex@706: # user checkbox defaults alex@706: for key, value in DefaultConfig.user_checkbox_defaults.items(): alex@706: if not self.user_checkbox_defaults.has_key(key): alex@706: self.user_checkbox_defaults[key] = value alex@706: alex@706: def __getitem__(self, item): alex@706: """ Make it possible to access a config object like a dict """ alex@706: return getattr(self, item) alex@706: alex@706: # remove the gettext pseudo function alex@706: del _ alex@706: