alex@706: # -*- coding: iso-8859-1 -*- alex@706: """ alex@706: MoinMoin - Multiple configuration handler and Configuration defaults class alex@706: tw@1918: @copyright: 2000-2004 Juergen Hermann , tw@1918: 2005-2006 MoinMoin:ThomasWaldmann. alex@706: @license: GNU GPL, see COPYING for details. alex@706: """ alex@706: alex@1053: import re alex@1053: import os alex@1053: import sys alex@1053: import time alex@1053: tw@1582: from MoinMoin import config, error, util, wikiutil alex@706: import MoinMoin.auth as authmodule alex@1096: from MoinMoin.packages import packLine tw@1276: from MoinMoin.security import AccessControlList 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: tw@915: msg = '''IndentationError: %(err)s 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. tw@915: ''' % { tw@915: 'err': err, tw@915: } 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: """ tw@931: 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: tw@915: msg = '''ImportError: %(err)s 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. tw@915: ''' % { tw@915: 'err': err, tw@915: } alex@706: raise error.ConfigurationError(msg) tw@915: except AttributeError, err: tw@915: msg = '''AttributeError: %(err)s tw@915: tw@915: Could not find required "Config" class in "%(name)s.py". tw@915: tw@915: This might happen if you are trying to use a pre 1.3 configuration file, or tw@915: made a syntax or spelling error. tw@915: tw@915: Another reason for this could be a name clash. It is not possible to have tw@915: config names like e.g. stats.py - because that colides with MoinMoin/stats/ - tw@915: have a look into your MoinMoin code directory what other names are NOT tw@915: possible. 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. tw@915: ''' % { tw@915: 'name': name, tw@915: 'err': err, tw@915: } 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 tw@1575: raise error.NoConfigMatchedError 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: """ tw@1582: cfgName = _getConfigName(url) alex@706: try: tw@1582: cfg = _config_cache[cfgName] alex@706: except KeyError: tw@1582: cfg = _makeConfig(cfgName) tw@1582: cfg = _config_cache.setdefault(cfgName, cfg) tw@1582: return cfg 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. tw@1920: def _(text): tw@1920: return text alex@706: alex@706: tw@1549: class CacheClass: tw@1549: """ just a container for stuff we cache """ tw@1549: pass tw@1549: tw@1549: alex@706: class DefaultConfig: alex@706: """ default config values """ tw@931: tw@1597: # setting DesktopEdition = True gives all local users special powers - ONLY use for MMDE style usage! tw@1597: DesktopEdition = False tw@1597: tw@729: # All acl_rights_* 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'] tw@931: alex@706: actions_excluded = [] # ['DeletePage', 'AttachFile', 'RenamePage', 'test', ] tw@1513: allow_xslt = False tw@1955: antispam_master_url = "http://moinmaster.wikiwikiweb.de:8000/?action=xmlrpc2" alex@706: attachments = None # {'dir': path, 'url': url-prefix} tw@931: auth = [authmodule.moin_login, authmodule.moin_session, ] tw@931: 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' tw@931: tw@1513: bang_meta = True alex@706: caching_formats = ['text_html'] alex@706: changed_time_fmt = '%H:%M' tw@729: alex@706: # chars_{upper,lower,digits,spaces} see MoinMoin/util/chartypes.py tw@729: alex@706: # if you have gdchart, add something like alex@706: # chart_options = {'width = 720, 'height': 540} alex@706: chart_options = None tw@931: tw@1513: config_check_enabled = False tw@770: 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 tw@931: alex@706: data_dir = './data/' alex@706: data_underlay_dir = './underlay/' tw@931: alex@706: date_fmt = '%Y-%m-%d' alex@706: datetime_fmt = '%Y-%m-%d %H:%M:%S' tw@931: alex@706: default_markup = 'wiki' alex@706: docbook_html_dir = r"/usr/share/xml/docbook/stylesheet/nwalsh/html/" # correct for debian sarge tw@931: tw@1752: edit_bar = ['Edit', 'Comments', 'Discussion', 'Info', 'Subscribe', 'Quicklink', 'Attachments', 'ActionsMenu'] 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 tw@931: 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 ' tw@1573: edit_ticketing = True alex@706: edit_rows = 20 tw@931: 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 = [] tw@931: 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: tw@838: log_reverse_dns_lookups = True # if we do reverse dns lookups for logging hostnames tw@838: # instead of just IPs tw@838: tw@1977: xapian_search = False fpletz@859: xapian_index_dir = None fpletz@916: xapian_stemming = True fpletz@1441: xapian_index_history = True fpletz@1205: search_results_per_page = 10 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 tw@1687: mail_import_pagename_search = ['subject', 'to', ] # where to look for target pagename (and in which order) tw@1615: mail_import_pagename_envelope = u"%s" # use u"+ %s/" to add "+ " and "/" automatically tw@1691: mail_import_pagename_regex = r'\["([^"]*)"\]' # how to find/extract the pagename from the subject tw@1658: mail_import_wiki_addrs = [] # the e-mail addresses for e-mails that should go into the wiki alex@718: mail_import_secret = "" tw@931: tw@1548: # some dangerous mimetypes (we don't use "content-disposition: inline" for them when a user tw@1548: # downloads such attachments, because the browser might execute e.g. Javascript contained tw@1548: # in the HTML and steal your moin cookie or do other nasty stuff) tw@1548: mimetypes_xss_protect = [ tw@1548: 'text/html', tw@1548: 'application/x-shockwave-flash', alex@1555: 'application/xhtml+xml', tw@1548: ] tw@1548: rb@1978: mimetypes_embed = [ rb@1978: 'application/x-shockwave-flash', rb@1978: 'application/x-dvi', rb@1978: 'application/postscript', rb@1978: 'application/pdf', rb@1978: 'application/ogg', rb@1978: 'application/vnd.visio', rb@1978: 'image/x-ms-bmp', rb@1978: 'image/svg+xml', rb@1978: 'image/tiff', rb@1978: 'image/x-photoshop', rb@1978: 'audio/mpeg', rb@1978: 'audio/midi', rb@1978: 'audio/x-wav', rb@1978: 'video/fli', rb@1978: 'video/mpeg', rb@1978: 'video/quicktime', rb@1978: 'video/x-msvideo', rb@1978: 'chemical/x-pdb', rb@1978: 'x-world/x-vrml', rb@1978: ] rb@1978: rb@1978: alex@706: navi_bar = [u'RecentChanges', u'FindPage', u'HelpContents', ] tw@1513: nonexist_qm = False 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 = '' tw@931: 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$' rb@1774: page_group_regex = u'[a-z0-9]Group$' alex@706: page_template_regex = u'[a-z0-9]Template$' alex@706: tw@1513: page_license_enabled = False 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. tw@1684: page_iconbar = ["edit", "view", "diff", "info", "subscribe", "raw", "print", ] alex@706: alex@706: # Standard buttons in the iconbar alex@706: page_icons_table = { tw@1684: # key querystr dict, title, icon-key tw@1684: 'diff': ({'action': 'diff'}, _("Diffs"), "diff"), tw@1684: 'info': ({'action': 'info'}, _("Info"), "info"), tw@1684: 'edit': ({'action': 'edit'}, _("Edit"), "edit"), tw@1684: 'unsubscribe': ({'action': 'subscribe'}, _("UnSubscribe"), "unsubscribe"), tw@1684: 'subscribe': ({'action': 'subscribe'}, _("Subscribe"), "subscribe"), tw@1684: 'raw': ({'action': 'raw'}, _("Raw"), "raw"), tw@1684: 'xml': ({'action': 'show', 'mimetype': 'text/xml'}, _("XML"), "xml"), tw@1684: 'print': ({'action': 'print'}, _("Print"), "print"), tw@1684: 'view': ({}, _("View"), "view"), alex@706: } tw@931: tw@1689: quicklinks_default = [] # preload user quicklinks with this page list alex@706: refresh = None # (minimum_delay, type), e.g.: (2, 'internal') alex@706: rss_cache = 60 # suggested caching time for RecentChanges RSS, in seconds tw@1959: sistersites = [ tw@1959: #('Self', 'http://localhost:8080/?action=sisterpages'), tw@1959: ('EmacsWiki', 'http://www.emacswiki.org/cgi-bin/test?action=sisterpages'), tw@1959: ('JspWiki', 'http://www.jspwiki.org/SisterSites.jsp'), tw@1959: ] # list of (sistersitename, sisterpagelistfetchurl) alex@706: shared_intermap = None # can be string or list of strings (filenames) tw@1513: show_hosts = True tw@1513: show_interwiki = False tw@1513: show_login = True alex@706: show_names = True alex@706: show_section_numbers = 0 tw@1513: show_timings = False tw@1513: show_version = False alex@706: siteid = 'default' alex@706: stylesheets = [] # list of tuples (media, csshref) to insert after theme css, before user css tw@1689: subscribed_pages_default = [] # preload user subscribed pages with this page list alex@706: superuser = [] # list of unicode user names that have super powers :) rb@1731: supplementation_page = False rb@1731: supplementation_page_name = u'Discussion' rb@1731: supplementation_page_template = u'DiscussionTemplate' tw@931: 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 tw@931: alex@706: theme_default = 'modern' alex@706: theme_force = False tw@931: alex@706: trail_size = 5 alex@706: tz_offset = 0.0 # default time zone offset in hours from UTC tw@931: 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|' tw@1397: 'linkbot|linkmap|linkwalk|mercator|mirror|msnbot|neomo|nutbot|omniexplorer|puf|robot|scooter|seekbot|' alex@706: 'sherlock|slurp|sitecheck|spider|teleport|voyager|webreaper|wget') alex@706: alex@706: # Wiki identity alex@706: sitename = u'Untitled Wiki' tw@1318: tw@1318: # url_prefix is DEPRECATED and not used any more by the code. tw@1318: # it confused many people by its name and default value of '/wiki' to the tw@1318: # wrong conclusion that it is the url of the wiki (the dynamic) stuff, tw@1318: # but it was used to address the static stuff (images, css, js). tw@1799: # Thus we use the more clear url_prefix_static ['/moin_staticVVV'] setting now. tw@1318: # For a limited time, we still look at url_prefix - if it is not None, we tw@1318: # copy the value to url_prefix_static to ease transition. tw@1318: url_prefix = None tw@1318: tw@1582: # includes the moin version number, so we can have a unlimited cache lifetime tw@1318: # for the static stuff. if stuff changes on version upgrade, url will change tw@1318: # immediately and we have no problem with stale caches. tw@1582: url_prefix_static = config.url_prefix_static tw@1654: url_prefix_local = None # if None, use same value as url_prefix_static. tw@1654: # must be same site as wiki engine (for e.g. JS permissions) tw@1318: alex@1417: # we could prefix actions to be able to exclude them by robots.txt: alex@1417: #url_prefix_action = 'action' # no leading or trailing '/' alex@1417: url_prefix_action = None # compatiblity tw@1318: alex@706: logo_string = None alex@706: interwikiname = None tw@931: 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')), tw@877: ('remember_last_visit', lambda _: _('After login, jump to last visited page')), tw@1752: ('show_comments', lambda _: _('Show comment sections')), 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')), tw@931: 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: ] tw@931: alex@706: user_checkbox_defaults = {'mailto_author': 0, alex@706: 'edit_on_doubleclick': 0, alex@706: 'remember_last_visit': 0, tw@1977: 'show_comments': 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: } tw@931: alex@706: # don't let the user change those alex@706: # user_checkbox_disable = ['disabled', 'want_trivial'] alex@706: user_checkbox_disable = [] tw@931: 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 = [] tw@931: 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", ''), tw@738: ('password2', _('Password repeat'), "password", "36", _('(Only for password change or new account)')), 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: ] tw@931: tw@931: user_form_defaults = {# key: default - do NOT remove keys from here! 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: } tw@931: 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 = [] tw@931: alex@706: # remove those completely: alex@706: #user_form_remove = ['password', 'password2', 'css_url', 'logout', 'create', 'account_sendmail',] alex@706: user_form_remove = [] tw@931: alex@706: # attributes we do NOT save to the userpref file tw@931: 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 tw@1977: unzip_attachments_count = 101 # 1 zip file + 100 files contained in it alex@706: tw@1513: xmlrpc_putpage_enabled = False # if False, putpage will write to a test page only tw@1513: xmlrpc_putpage_trusted_only = True # if True, you will need to be http auth authenticated tw@931: alex@706: SecurityPolicy = None alex@706: alex@706: def __init__(self, siteid): alex@706: """ Init Config instance """ alex@706: self.siteid = siteid tw@1549: self.cache = CacheClass() tw@1549: tw@1624: from MoinMoin.Page import ItemCache tw@1624: self.cache.meta = ItemCache('meta') tw@1624: self.cache.pagelists = ItemCache('pagelists') tw@1620: alex@706: if self.config_check_enabled: alex@706: self._config_check() alex@706: alex@706: # define directories tw@1011: self.moinmoin_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) 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): johannes@1929: setattr(self, name, os.path.abspath(os.path.join(data_dir, dirname))) fpletz@855: alex@706: # Try to decode certain names which allow unicode alex@706: self._decode() alex@706: tw@1549: # After that, pre-compile some regexes tw@1549: self.cache.page_category_regex = re.compile(self.page_category_regex, re.UNICODE) tw@1549: self.cache.page_dict_regex = re.compile(self.page_dict_regex, re.UNICODE) tw@1549: self.cache.page_group_regex = re.compile(self.page_group_regex, re.UNICODE) tw@1549: self.cache.page_template_regex = re.compile(self.page_template_regex, re.UNICODE) alex@1557: self.cache.ua_spiders = self.ua_spiders and re.compile(self.ua_spiders, re.I) tw@1549: 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) tw@931: alex@706: self._loadPluginModule() alex@706: alex@706: # Preparse user dicts alex@706: self._fillDicts() tw@931: 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 tw@931: 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: tw@788: # list to cache xapian searcher objects tw@788: self.xapian_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@1080: alex@1118: # Cache variables for the properties below alex@1118: self._iwid = self._iwid_full = self._meta_dict = None alex@1118: tw@1552: self.cache.acl_rights_before = AccessControlList(self, [self.acl_rights_before]) tw@1552: self.cache.acl_rights_default = AccessControlList(self, [self.acl_rights_default]) tw@1552: self.cache.acl_rights_after = AccessControlList(self, [self.acl_rights_after]) tw@1276: tw@1318: if self.url_prefix is not None: # remove this code when url_prefix setting is removed tw@1318: self.url_prefix_static = self.url_prefix tw@1318: tw@1339: action_prefix = self.url_prefix_action tw@1339: if action_prefix is not None and action_prefix.endswith('/'): # make sure there is no trailing '/' tw@1339: self.url_prefix_action = action_prefix[:-1] tw@1339: tw@1654: if self.url_prefix_local is None: tw@1654: self.url_prefix_local = self.url_prefix_static tw@1654: tw@1654: alex@1118: def load_meta_dict(self): alex@1118: """ The meta_dict contains meta data about the wiki instance. """ alex@1118: if getattr(self, "_meta_dict", None) is None: alex@1118: self._meta_dict = wikiutil.MetaDict(os.path.join(self.data_dir, 'meta'), self.cache_dir) alex@1118: return self._meta_dict alex@1118: meta_dict = property(load_meta_dict) alex@1118: alex@1118: # lazily load iwid(_full) alex@1118: def make_iwid_property(attr): alex@1118: def getter(self): alex@1118: if getattr(self, attr, None) is None: alex@1118: self.load_IWID() alex@1118: return getattr(self, attr) alex@1118: return property(getter) alex@1118: iwid = make_iwid_property("_iwid") alex@1118: iwid_full = make_iwid_property("_iwid_full") alex@1053: alex@1053: def load_IWID(self): alex@1053: """ Loads the InterWikiID of this instance. It is used to identify the instance alex@1053: globally. alex@1053: The IWID is available as cfg.iwid alex@1053: The full IWID containing the interwiki name is available as cfg.iwid_full alex@1118: This method is called by the property. alex@1053: """ alex@1053: alex@1053: try: alex@1080: iwid = self.meta_dict['IWID'] alex@1080: except KeyError: alex@1053: iwid = util.random_string(16).encode("hex") + "-" + str(int(time.time())) alex@1080: self.meta_dict['IWID'] = iwid alex@1080: self.meta_dict.sync() alex@1053: alex@1118: self._iwid = iwid alex@1053: if self.interwikiname is not None: alex@1118: self._iwid_full = packLine([iwid, self.interwikiname]) alex@1053: else: alex@1118: self._iwid_full = packLine([iwid]) 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. tw@931: """ alex@706: unknown = ['"%s"' % name for name in dir(self) tw@931: if not name.startswith('_') and tw@1868: name not in DefaultConfig.__dict__ 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: ''' tw@931: alex@706: decode_names = ( alex@706: 'sitename', 'logo_string', 'navi_bar', 'page_front_page', tw@931: '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: ) tw@931: 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: tw@931: 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) tw@931: 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. tw@931: ''' % {'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: """ tw@1791: import 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. tw@915: ''' % { tw@915: 'path': self.data_dir, tw@915: 'err': str(err), tw@915: } 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(): tw@1868: if key not in self.user_checkbox_defaults: 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) tw@931: alex@706: # remove the gettext pseudo function alex@706: del _ alex@706: