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