--- a/__pkginfo__.py Wed May 05 18:10:07 2010 +0200
+++ b/__pkginfo__.py Wed May 05 18:10:33 2010 +0200
@@ -40,7 +40,7 @@
]
__depends__ = {
- 'logilab-common': '>= 0.50.0',
+ 'logilab-common': '>= 0.50.1',
'logilab-mtconverter': '>= 0.6.0',
'rql': '>= 0.26.0',
'yams': '>= 0.28.1',
--- a/cwconfig.py Wed May 05 18:10:07 2010 +0200
+++ b/cwconfig.py Wed May 05 18:10:33 2010 +0200
@@ -293,8 +293,6 @@
log_format = '%(asctime)s - (%(name)s) %(levelname)s: %(message)s'
# nor remove appobjects based on unused interface
cleanup_interface_sobjects = True
- # debug mode
- debugmode = False
if (CWDEV and _forced_mode != 'system'):
@@ -660,12 +658,14 @@
vregpath.append(path + '.py')
return vregpath
- def __init__(self):
+ def __init__(self, debugmode=False):
register_stored_procedures()
ConfigurationMixIn.__init__(self)
+ self.debugmode = debugmode
self.adjust_sys_path()
self.load_defaults()
- self.translations = {}
+ # will be properly initialized later by _gettext_init
+ self.translations = {'en': (unicode, lambda ctx, msgid: unicode(msgid) )}
# don't register ReStructured Text directives by simple import, avoid pb
# with eg sphinx.
# XXX should be done properly with a function from cw.uicfg
@@ -680,16 +680,14 @@
# overriden in CubicWebConfiguration
self.cls_adjust_sys_path()
- def init_log(self, logthreshold=None, debug=False,
- logfile=None, syslog=False):
+ def init_log(self, logthreshold=None, logfile=None, syslog=False):
"""init the log service"""
if logthreshold is None:
- if debug:
+ if self.debugmode:
logthreshold = 'DEBUG'
else:
logthreshold = self['log-threshold']
- self.debugmode = debug
- init_log(debug, syslog, logthreshold, logfile, self.log_format)
+ init_log(self.debugmode, syslog, logthreshold, logfile, self.log_format)
# configure simpleTal logger
logging.getLogger('simpleTAL').setLevel(logging.ERROR)
@@ -803,12 +801,12 @@
return mdir
@classmethod
- def config_for(cls, appid, config=None):
+ def config_for(cls, appid, config=None, debugmode=False):
"""return a configuration instance for the given instance identifier
"""
config = config or guess_configuration(cls.instance_home(appid))
configcls = configuration_cls(config)
- return configcls(appid)
+ return configcls(appid, debugmode)
@classmethod
def possible_configurations(cls, appid):
@@ -876,9 +874,9 @@
# instance methods used to get instance specific resources #############
- def __init__(self, appid):
+ def __init__(self, appid, debugmode=False):
self.appid = appid
- CubicWebNoAppConfiguration.__init__(self)
+ CubicWebNoAppConfiguration.__init__(self, debugmode)
self._cubes = None
self._site_loaded = set()
self.load_file_configuration(self.main_config_file())
@@ -988,14 +986,14 @@
super(CubicWebConfiguration, self).load_configuration()
if self.apphome and self.set_language:
# init gettext
- self._set_language()
+ self._gettext_init()
- def init_log(self, logthreshold=None, debug=False, force=False):
+ def init_log(self, logthreshold=None, force=False):
"""init the log service"""
if not force and hasattr(self, '_logging_initialized'):
return
self._logging_initialized = True
- CubicWebNoAppConfiguration.init_log(self, logthreshold, debug,
+ CubicWebNoAppConfiguration.init_log(self, logthreshold,
logfile=self.get('log-file'))
# read a config file if it exists
logconfig = join(self.apphome, 'logging.conf')
@@ -1016,7 +1014,7 @@
if lang != 'en':
yield lang
- def _set_language(self):
+ def _gettext_init(self):
"""set language for gettext"""
from gettext import translation
path = join(self.apphome, 'i18n')
--- a/cwctl.py Wed May 05 18:10:07 2010 +0200
+++ b/cwctl.py Wed May 05 18:10:33 2010 +0200
@@ -477,14 +477,13 @@
def start_instance(self, appid):
"""start the instance's server"""
- debug = self['debug']
force = self['force']
loglevel = self['loglevel']
- config = cwcfg.config_for(appid)
+ config = cwcfg.config_for(appid, debugmode=self['debug'])
if loglevel is not None:
loglevel = 'LOG_%s' % loglevel.upper()
config.global_set_option('log-threshold', loglevel)
- config.init_log(loglevel, debug=debug, force=True)
+ config.init_log(loglevel, force=True)
if self['profile']:
config.global_set_option('profile', self.config.profile)
helper = self.config_helper(config, cmdname='start')
@@ -493,7 +492,7 @@
msg = "%s seems to be running. Remove %s by hand if necessary or use \
the --force option."
raise ExecutionError(msg % (appid, pidf))
- helper.start_server(config, debug)
+ helper.start_server(config)
class StopInstanceCommand(InstanceCommand):
@@ -781,11 +780,15 @@
repository internals (session, etc...) so most migration commands won't be
available.
+ Arguments after bare "--" string will not be processed by the shell command
+ You can use it to pass extra arguments to your script and expect for
+ them in '__args__' afterwards.
+
<instance>
the identifier of the instance to connect.
"""
name = 'shell'
- arguments = '<instance> [batch command file]'
+ arguments = '<instance> [batch command file(s)] [-- <script arguments>]'
options = (
('system-only',
{'short': 'S', 'action' : 'store_true',
@@ -861,8 +864,11 @@
mih = config.migration_handler()
try:
if args:
- for arg in args:
- mih.cmd_process_script(arg)
+ # use cmdline parser to access left/right attributes only
+ # remember that usage requires instance appid as first argument
+ scripts, args = self.cmdline_parser.largs[1:], self.cmdline_parser.rargs
+ for script in scripts:
+ mih.cmd_process_script(script, scriptargs=args)
else:
mih.interactive_shell()
finally:
--- a/cwvreg.py Wed May 05 18:10:07 2010 +0200
+++ b/cwvreg.py Wed May 05 18:10:33 2010 +0200
@@ -442,14 +442,13 @@
* contentnavigation XXX to merge with components? to kill?
"""
- def __init__(self, config, debug=None, initlog=True):
+ def __init__(self, config, initlog=True):
if initlog:
# first init log service
- config.init_log(debug=debug)
+ config.init_log()
super(CubicWebVRegistry, self).__init__(config)
self.schema = None
self.initialized = False
- self.reset()
# XXX give force_reload (or refactor [re]loading...)
if self.config.mode != 'test':
# don't clear rtags during test, this may cause breakage with
@@ -519,7 +518,6 @@
if not cube in cubes:
cpath = cfg.build_vregistry_cube_path([cfg.cube_dir(cube)])
cleanup_sys_modules(cpath)
- self.reset()
self.register_objects(path, force_reload)
CW_EVENT_MANAGER.emit('after-registry-reload')
--- a/dbapi.py Wed May 05 18:10:07 2010 +0200
+++ b/dbapi.py Wed May 05 18:10:33 2010 +0200
@@ -253,9 +253,6 @@
self.session = None
self.cnx = self.user = _NeedAuthAccessMock()
- def base_url(self):
- return self.vreg.config['base-url']
-
def from_controller(self):
return 'view'
--- a/debian/control Wed May 05 18:10:07 2010 +0200
+++ b/debian/control Wed May 05 18:10:33 2010 +0200
@@ -97,7 +97,7 @@
Package: cubicweb-common
Architecture: all
XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.50.0), python-yams (>= 0.29.0), python-rql (>= 0.26.0), python-lxml
+Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.50.1), python-yams (>= 0.29.0), python-rql (>= 0.26.0), python-lxml
Recommends: python-simpletal (>= 4.0), python-crypto
Conflicts: cubicweb-core
Replaces: cubicweb-core
--- a/devtools/__init__.py Wed May 05 18:10:07 2010 +0200
+++ b/devtools/__init__.py Wed May 05 18:10:33 2010 +0200
@@ -181,10 +181,6 @@
def available_languages(self, *args):
return ('en', 'fr', 'de')
- def ext_resources_file(self):
- """return instance's external resources file"""
- return join(self.apphome, 'data', 'external_resources')
-
def pyro_enabled(self):
# but export PYRO_MULTITHREAD=0 or you get problems with sqlite and threads
return True
--- a/devtools/devctl.py Wed May 05 18:10:07 2010 +0200
+++ b/devtools/devctl.py Wed May 05 18:10:33 2010 +0200
@@ -60,13 +60,14 @@
self.expand_cubes(cubes, with_recommends=True))
else:
self._cubes = ()
+ self.uiprops = {'FCKEDITOR_PATH': ''}
@property
def apphome(self):
return None
def main_config_file(self):
return None
- def init_log(self, debug=None):
+ def init_log(self):
pass
def load_configuration(self):
pass
@@ -596,7 +597,7 @@
exclude = SKEL_EXCLUDE
if self['layout'] == 'simple':
exclude += ('sobjects.py*', 'precreate.py*', 'realdb_test*',
- 'cubes.*', 'external_resources*')
+ 'cubes.*', 'uiprops.py*')
copy_skeleton(skeldir, cubedir, context, exclude=exclude)
def _ask_for_dependencies(self):
--- a/devtools/fake.py Wed May 05 18:10:07 2010 +0200
+++ b/devtools/fake.py Wed May 05 18:10:33 2010 +0200
@@ -30,6 +30,7 @@
class FakeConfig(dict, BaseApptestConfiguration):
translations = {}
+ uiprops = {}
apphome = None
def __init__(self, appid='data', apphome=None, cubes=()):
self.appid = appid
@@ -39,6 +40,7 @@
self['uid'] = None
self['base-url'] = BASE_URL
self['rql-cache-size'] = 100
+ self.datadir_url = BASE_URL + 'data/'
def cubes(self, expand=False):
return self._cubes
@@ -66,10 +68,6 @@
def header_if_modified_since(self):
return None
- def base_url(self):
- """return the root url of the instance"""
- return BASE_URL
-
def relative_path(self, includeparams=True):
"""return the normalized path of the request (ie at least relative
to the instance's root, but some other normalization may be needed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/annexes/docstrings-conventions.rst Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,106 @@
+Javascript docstrings
+=====================
+
+Whereas in Python source code we only need to include a module docstrings
+using the directive `.. automodule:: mypythonmodule`, we will have to
+explicitely define Javascript modules and functions in the doctrings since
+there is no native directive to include Javascript files.
+
+Rest generation
+---------------
+
+`pyjsrest` is a small utility parsing Javascript doctrings and generating the
+corresponding Restructured file used by Sphinx to generate HTML documentation.
+This script will have the following structure::
+
+ ===========
+ filename.js
+ ===========
+ .. module:: filename.js
+
+We use the `.. module::` directive to register a javascript library
+as a Python module for Sphinx. This provides an entry in the module index.
+
+The contents of the docstring found in the javascript file will be added as is
+following the module declaration. No treatment will be done on the doctring.
+All the documentation structure will be in the docstrings and will comply
+with the following rules.
+
+Docstring structure
+-------------------
+
+Basically we document javascript with RestructuredText docstring
+following the same convention as documenting Python code.
+
+The doctring in Javascript files must be contained in standard
+Javascript comment signs, starting with `/**` and ending with `*/`,
+such as::
+
+ /**
+ * My comment starts here.
+ * This is the second line prefixed with a `*`.
+ * ...
+ * ...
+ * All the follwing line will be prefixed with a `*` followed by a space.
+ * ...
+ * ...
+ */
+
+
+Comments line prefixed by `//` will be ignored. They are reserved for source
+code comments dedicated to developers.
+
+
+Javscript functions docstring
+-----------------------------
+
+By default, the `function` directive describes a module-level function.
+
+`function` directive
+~~~~~~~~~~~~~~~~~~~~
+
+Its purpose is to define the function prototype such as::
+
+ .. function:: loadxhtml(url, data, reqtype, mode)
+
+If any namespace is used, we should add it in the prototype for now,
+until we define an appropriate directive.
+::
+ .. function:: jQuery.fn.loadxhtml(url, data, reqtype, mode)
+
+Function parameters
+~~~~~~~~~~~~~~~~~~~
+
+We will define function parameters as a bulleted list, where the
+parameter name will be backquoted and followed by its description.
+
+Example of a javascript function docstring::
+
+ .. function:: loadxhtml(url, data, reqtype, mode)
+
+ cubicweb loadxhtml plugin to make jquery handle xhtml response
+
+ fetches `url` and replaces this's content with the result
+
+ Its arguments are:
+
+ * `url`
+
+ * `mode`, how the replacement should be done (default is 'replace')
+ Possible values are :
+ - 'replace' to replace the node's content with the generated HTML
+ - 'swap' to replace the node itself with the generated HTML
+ - 'append' to append the generated HTML to the node's content
+
+
+Optional parameter specification
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Javascript functions handle arguments not listed in the function signature.
+In the javascript code, they will be flagged using `/* ... */`. In the docstring,
+we flag those optional arguments the same way we would define it in
+Python::
+
+ .. function:: asyncRemoteExec(fname, arg1=None, arg2=None)
+
+
--- a/doc/book/en/annexes/index.rst Wed May 05 18:10:07 2010 +0200
+++ b/doc/book/en/annexes/index.rst Wed May 05 18:10:33 2010 +0200
@@ -17,6 +17,8 @@
rql/index
mercurial
depends
+ javascript-api
+ docstrings-conventions
(X)HTML tricks to apply
-----------------------
--- a/doc/book/en/devweb/js.rst Wed May 05 18:10:07 2010 +0200
+++ b/doc/book/en/devweb/js.rst Wed May 05 18:10:33 2010 +0200
@@ -353,3 +353,11 @@
There is also javascript support for massmailing, gmap (google maps),
fckcwconfig (fck editor), timeline, calendar, goa (CubicWeb over
AppEngine), flot (charts drawing), tabs and bookmarks.
+
+API
+~~~
+
+.. toctree::
+ :maxdepth: 1
+
+ js_api/index
--- a/doc/book/en/makefile Wed May 05 18:10:07 2010 +0200
+++ b/doc/book/en/makefile Wed May 05 18:10:33 2010 +0200
@@ -11,6 +11,10 @@
PAPER =
#BUILDDIR = build
BUILDDIR = ~/tmp/cwdoc
+CWDIR = ../../..
+JSDIR = ${CWDIR}/web/data
+JSTORST = ${CWDIR}/doc/tools/pyjsrest.py
+BUILDJS = devweb/js_api
# Internal variables for sphinx
PAPEROPT_a4 = -D latex_paper_size=a4
@@ -18,6 +22,7 @@
ALLSPHINXOPTS = -d ${BUILDDIR}/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
help:
@@ -36,6 +41,7 @@
rm -rf apidoc/
rm -f *.html
-rm -rf ${BUILDDIR}/*
+ -rm -rf ${BUILDJS}
all: ${TARGET} apidoc html
@@ -48,12 +54,16 @@
epydoc --html -o apidoc -n "cubicweb" --exclude=setup --exclude=__pkginfo__ ../../../
# run sphinx ###
-html:
+html: js
mkdir -p ${BUILDDIR}/html ${BUILDDIR}/doctrees
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) ${BUILDDIR}/html
@echo
@echo "Build finished. The HTML pages are in ${BUILDDIR}/html."
+js:
+ mkdir -p ${BUILDJS}
+ $(JSTORST) -p ${JSDIR} -o ${BUILDJS}
+
pickle:
mkdir -p ${BUILDDIR}/pickle ${BUILDDIR}/doctrees
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) ${BUILDDIR}/pickle
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tools/pyjsrest.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+"""
+Parser for Javascript comments.
+"""
+from __future__ import with_statement
+
+import sys, os, getopt, re
+
+def clean_comment(match):
+ comment = match.group()
+ comment = strip_stars(comment)
+ return comment
+
+# Rest utilities
+def rest_title(title, level, level_markups=['=', '=', '-', '~', '+', '`']):
+ size = len(title)
+ if level == 0:
+ return '\n'.join((level_markups[level] * size, title, level_markups[0] * size)) + '\n'
+ return '\n'.join(('\n' + title, level_markups[level] * size)) + '\n'
+
+def get_doc_comments(text):
+ """
+ Return a list of all documentation comments in the file text. Each
+ comment is a pair, with the first element being the comment text and
+ the second element being the line after it, which may be needed to
+ guess function & arguments.
+
+ >>> get_doc_comments(read_file('examples/module.js'))[0][0][:40]
+ '/**\n * This is the module documentation.'
+ >>> get_doc_comments(read_file('examples/module.js'))[1][0][7:50]
+ 'This is documentation for the first method.'
+ >>> get_doc_comments(read_file('examples/module.js'))[1][1]
+ 'function the_first_function(arg1, arg2) '
+ >>> get_doc_comments(read_file('examples/module.js'))[2][0]
+ '/** This is the documentation for the second function. */'
+
+ """
+ return [clean_comment(match) for match in re.finditer('/\*\*.*?\*/',
+ text, re.DOTALL|re.MULTILINE)]
+
+RE_STARS = re.compile('^\s*?\* ?', re.MULTILINE)
+
+
+def strip_stars(doc_comment):
+ """
+ Strip leading stars from a doc comment.
+
+ >>> strip_stars('/** This is a comment. */')
+ 'This is a comment.'
+ >>> strip_stars('/**\n * This is a\n * multiline comment. */')
+ 'This is a\n multiline comment.'
+ >>> strip_stars('/** \n\t * This is a\n\t * multiline comment. \n*/')
+ 'This is a\n multiline comment.'
+
+ """
+ return RE_STARS.sub('', doc_comment[3:-2]).strip()
+
+def parse_js_files(args=sys.argv):
+ """
+ Main command-line invocation.
+ """
+ try:
+ opts, args = getopt.gnu_getopt(args[1:], 'p:o:h', [
+ 'jspath=', 'output=', 'help'])
+ opts = dict(opts)
+ except getopt.GetoptError:
+ usage()
+ sys.exit(2)
+
+ rst_dir = opts.get('--output') or opts.get('-o')
+ if rst_dir is None and len(args) != 1:
+ rst_dir = 'apidocs'
+ js_dir = opts.get('--jspath') or opts.get('-p')
+ if not os.path.exists(os.path.join(rst_dir)):
+ os.makedirs(os.path.join(rst_dir))
+
+ f_index = open(os.path.join(rst_dir, 'index.rst'), 'wb')
+ f_index.write('''
+.. toctree::
+ :maxdepth: 1
+
+'''
+)
+ for js_path, js_dirs, js_files in os.walk(js_dir):
+ rst_path = re.sub('%s%s*' % (js_dir, os.path.sep), '', js_path)
+ for js_file in js_files:
+ if not js_file.endswith('.js'):
+ continue
+ if not os.path.exists(os.path.join(rst_dir, rst_path)):
+ os.makedirs(os.path.join(rst_dir, rst_path))
+ rst_content = extract_rest(js_path, js_file)
+ filename = os.path.join(rst_path, js_file[:-3])
+ # add to index
+ f_index.write(' %s\n' % filename)
+ # save rst file
+ with open(os.path.join(rst_dir, filename) + '.rst', 'wb') as f_rst:
+ f_rst.write(rst_content)
+ f_index.close()
+
+def extract_rest(js_dir, js_file):
+ js_filepath = os.path.join(js_dir, js_file)
+ filecontent = open(js_filepath, 'U').read()
+ comments = get_doc_comments(filecontent)
+ rst = rest_title(js_file, 0)
+ rst += '.. module:: %s\n\n' % js_file
+ rst += '\n\n'.join(comments)
+ return rst
+
+if __name__ == '__main__':
+ parse_js_files()
--- a/etwist/request.py Wed May 05 18:10:07 2010 +0200
+++ b/etwist/request.py Wed May 05 18:10:33 2010 +0200
@@ -15,9 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""Twisted request handler for CubicWeb
+"""Twisted request handler for CubicWeb"""
-"""
__docformat__ = "restructuredtext en"
from datetime import datetime
@@ -55,9 +54,9 @@
return self._twreq.method
def relative_path(self, includeparams=True):
- """return the normalized path of the request (ie at least relative
- to the instance's root, but some other normalization may be needed
- so that the returned path may be used to compare to generated urls
+ """return the normalized path of the request (ie at least relative to
+ the instance's root, but some other normalization may be needed so that
+ the returned path may be used to compare to generated urls
:param includeparams:
boolean indicating if GET form parameters should be kept in the path
@@ -68,8 +67,8 @@
return path
def get_header(self, header, default=None, raw=True):
- """return the value associated with the given input header,
- raise KeyError if the header is not set
+ """return the value associated with the given input header, raise
+ KeyError if the header is not set
"""
if raw:
return self._headers_in.getRawHeaders(header, [default])[0]
--- a/etwist/server.py Wed May 05 18:10:07 2010 +0200
+++ b/etwist/server.py Wed May 05 18:10:33 2010 +0200
@@ -122,12 +122,11 @@
class CubicWebRootResource(resource.Resource):
- def __init__(self, config, debug=None):
- self.debugmode = debug
+ def __init__(self, config):
self.config = config
# instantiate publisher here and not in init_publisher to get some
# checks done before daemonization (eg versions consistency)
- self.appli = CubicWebPublisher(config, debug=self.debugmode)
+ self.appli = CubicWebPublisher(config)
self.base_url = config['base-url']
self.https_url = config['https-url']
self.children = {}
@@ -179,6 +178,9 @@
pre_path = request.path.split('/')[1:]
if pre_path[0] == 'https':
pre_path.pop(0)
+ uiprops = self.config.https_uiprops
+ else:
+ uiprops = self.config.uiprops
directory = pre_path[0]
# Anything in data/, static/, fckeditor/ and the generated versioned
# data directory is treated as static files
@@ -188,7 +190,7 @@
if directory == 'static':
return File(self.config.static_directory)
if directory == 'fckeditor':
- return File(self.config.ext_resources['FCKEDITOR_PATH'])
+ return File(uiprops['FCKEDITOR_PATH'])
if directory != 'data':
# versioned directory, use specific file with http cache
# headers so their are cached for a very long time
@@ -196,7 +198,7 @@
else:
cls = File
if path == 'fckeditor':
- return cls(self.config.ext_resources['FCKEDITOR_PATH'])
+ return cls(uiprops['FCKEDITOR_PATH'])
if path == directory: # recurse
return self
datadir = self.config.locate_resource(path)
@@ -210,7 +212,10 @@
def render(self, request):
"""Render a page from the root resource"""
# reload modified files in debug mode
- if self.debugmode:
+ if self.config.debugmode:
+ self.config.uiprops.reload_if_needed()
+ if self.https_url:
+ self.config.https_uiprops.reload_if_needed()
self.appli.vreg.reload_if_needed()
if self.config['profile']: # default profiler don't trace threads
return self.render_request(request)
@@ -405,15 +410,15 @@
LOGGER = getLogger('cubicweb.twisted')
set_log_methods(CubicWebRootResource, LOGGER)
-def run(config, debug):
+def run(config):
# create the site
- root_resource = CubicWebRootResource(config, debug)
+ root_resource = CubicWebRootResource(config)
website = server.Site(root_resource)
# serve it via standard HTTP on port set in the configuration
port = config['port'] or 8080
reactor.listenTCP(port, website)
logger = getLogger('cubicweb.twisted')
- if not debug:
+ if not config.debugmode:
if sys.platform == 'win32':
raise ConfigurationError("Under windows, you must use the service management "
"commands (e.g : 'net start my_instance)'")
--- a/etwist/twctl.py Wed May 05 18:10:07 2010 +0200
+++ b/etwist/twctl.py Wed May 05 18:10:33 2010 +0200
@@ -32,9 +32,9 @@
cmdname = 'start'
cfgname = 'twisted'
- def start_server(self, config, debug):
+ def start_server(self, config):
from cubicweb.etwist import server
- server.run(config, debug)
+ server.run(config)
class TWStopHandler(CommandHandler):
cmdname = 'stop'
--- a/goa/skel/loader.py Wed May 05 18:10:07 2010 +0200
+++ b/goa/skel/loader.py Wed May 05 18:10:33 2010 +0200
@@ -30,7 +30,7 @@
# apply monkey patches first
goa.do_monkey_patch()
# get instance's configuration (will be loaded from app.conf file)
- GAEConfiguration.ext_resources['JAVASCRIPTS'].append('DATADIR/goa.js')
+ GAEConfiguration.uiprops['JAVASCRIPTS'].append('DATADIR/goa.js')
config = GAEConfiguration('toto', APPLROOT)
# create default groups
create_groups()
--- a/goa/skel/main.py Wed May 05 18:10:07 2010 +0200
+++ b/goa/skel/main.py Wed May 05 18:10:33 2010 +0200
@@ -31,7 +31,7 @@
# get instance's configuration (will be loaded from app.conf file)
from cubicweb.goa.goaconfig import GAEConfiguration
-GAEConfiguration.ext_resources['JAVASCRIPTS'].append('DATADIR/goa.js')
+GAEConfiguration.uiprops['JAVASCRIPTS'].append('DATADIR/goa.js')
config = GAEConfiguration('toto', APPLROOT)
# dynamic objects registry
--- a/goa/tools/laxctl.py Wed May 05 18:10:07 2010 +0200
+++ b/goa/tools/laxctl.py Wed May 05 18:10:33 2010 +0200
@@ -43,7 +43,7 @@
do_monkey_patch()
from cubicweb.goa.goavreg import GAEVregistry
from cubicweb.goa.goaconfig import GAEConfiguration
- #WebConfiguration.ext_resources['JAVASCRIPTS'].append('DATADIR/goa.js')
+ #WebConfiguration.uiprops['JAVASCRIPTS'].append('DATADIR/goa.js')
config = GAEConfiguration('toto', applroot)
vreg = GAEVregistry(config)
vreg.set_schema(config.load_schema())
--- a/migration.py Wed May 05 18:10:07 2010 +0200
+++ b/migration.py Wed May 05 18:10:33 2010 +0200
@@ -111,7 +111,7 @@
self.config = config
if config:
# no config on shell to a remote instance
- self.config.init_log(logthreshold=logging.ERROR, debug=True)
+ self.config.init_log(logthreshold=logging.ERROR)
# 0: no confirmation, 1: only main commands confirmed, 2 ask for everything
self.verbosity = verbosity
self.need_wrap = True
@@ -281,14 +281,25 @@
return context
def cmd_process_script(self, migrscript, funcname=None, *args, **kwargs):
- """execute a migration script
- in interactive mode, display the migration script path, ask for
- confirmation and execute it if confirmed
+ """execute a migration script in interactive mode
+
+ Display the migration script path, ask for confirmation and execute it
+ if confirmed
+
+ Context environment can have these variables defined:
+ - __name__ : will be determine by funcname parameter
+ - __file__ : is the name of the script if it exists
+ - __args__ : script arguments coming from command-line
+
+ :param migrscript: name of the script
+ :param funcname: defines __name__ inside the shell (or use __main__)
+ :params args: optional arguments for funcname
+ :keyword scriptargs: optional arguments of the script
"""
migrscript = os.path.normpath(migrscript)
if migrscript.endswith('.py'):
script_mode = 'python'
- elif migrscript.endswith('.txt') or migrscript.endswith('.rst'):
+ elif migrscript.endswith(('.txt', '.rst')):
script_mode = 'doctest'
else:
raise Exception('This is not a valid cubicweb shell input')
@@ -300,7 +311,8 @@
pyname = '__main__'
else:
pyname = splitext(basename(migrscript))[0]
- scriptlocals.update({'__file__': migrscript, '__name__': pyname})
+ scriptlocals.update({'__file__': migrscript, '__name__': pyname,
+ '__args__': kwargs.pop("scriptargs", [])})
execfile(migrscript, scriptlocals)
if funcname is not None:
try:
--- a/req.py Wed May 05 18:10:07 2010 +0200
+++ b/req.py Wed May 05 18:10:33 2010 +0200
@@ -371,11 +371,11 @@
raise ValueError(self._('can\'t parse %(value)r (expected %(format)s)')
% {'value': value, 'format': format})
- # abstract methods to override according to the web front-end #############
-
def base_url(self):
"""return the root url of the instance"""
- raise NotImplementedError
+ return self.vreg.config['base-url']
+
+ # abstract methods to override according to the web front-end #############
def describe(self, eid):
"""return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
--- a/selectors.py Wed May 05 18:10:07 2010 +0200
+++ b/selectors.py Wed May 05 18:10:33 2010 +0200
@@ -594,7 +594,7 @@
class multi_lines_rset(Selector):
- """If `nb`is specified, return 1 if the result set has exactly `nb` row of
+ """If `nb` is specified, return 1 if the result set has exactly `nb` row of
result. Else (`nb` is None), return 1 if the result set contains *at least*
two rows.
"""
@@ -612,7 +612,7 @@
class multi_columns_rset(multi_lines_rset):
- """If `nb`is specified, return 1 if the result set has exactly `nb` column
+ """If `nb` is specified, return 1 if the result set has exactly `nb` column
per row. Else (`nb` is None), return 1 if the result set contains *at least*
two columns per row. Return 0 for empty result set.
"""
--- a/server/repository.py Wed May 05 18:10:07 2010 +0200
+++ b/server/repository.py Wed May 05 18:10:33 2010 +0200
@@ -104,10 +104,10 @@
XXX protect pyro access
"""
- def __init__(self, config, vreg=None, debug=False):
+ def __init__(self, config, vreg=None):
self.config = config
if vreg is None:
- vreg = cwvreg.CubicWebVRegistry(config, debug)
+ vreg = cwvreg.CubicWebVRegistry(config)
self.vreg = vreg
self.pyro_registered = False
self.info('starting repository from %s', self.config.apphome)
@@ -152,13 +152,6 @@
if not isinstance(session.user, InternalManager):
session.user.__class__ = usercls
- def _bootstrap_hook_registry(self):
- """called during bootstrap since we need the metadata hooks"""
- hooksdirectory = join(CW_SOFTWARE_ROOT, 'hooks')
- self.vreg.init_registration([hooksdirectory])
- self.vreg.load_file(join(hooksdirectory, 'metadata.py'),
- 'cubicweb.hooks.metadata')
-
def open_connections_pools(self):
config = self.config
self._available_pools = Queue.Queue()
@@ -184,7 +177,9 @@
for modname in ('__init__', 'authobjs', 'wfobjs'):
self.vreg.load_file(join(etdirectory, '%s.py' % modname),
'cubicweb.entities.%s' % modname)
- self._bootstrap_hook_registry()
+ hooksdirectory = join(CW_SOFTWARE_ROOT, 'hooks')
+ self.vreg.load_file(join(hooksdirectory, 'metadata.py'),
+ 'cubicweb.hooks.metadata')
elif config.read_instance_schema:
# normal start: load the instance schema from the database
self.fill_schema()
@@ -233,8 +228,7 @@
if resetvreg:
if self.config._cubes is None:
self.config.init_cubes(self.get_cubes())
- # full reload of all appobjects
- self.vreg.reset()
+ # trigger full reload of all appobjects
self.vreg.set_schema(schema)
else:
self.vreg._set_schema(schema)
--- a/server/serverctl.py Wed May 05 18:10:07 2010 +0200
+++ b/server/serverctl.py Wed May 05 18:10:33 2010 +0200
@@ -249,9 +249,9 @@
cmdname = 'start'
cfgname = 'repository'
- def start_server(self, ctlconf, debug):
+ def start_server(self, ctlconf):
command = ['cubicweb-ctl start-repository ']
- if debug:
+ if ctlconf.debugmode:
command.append('--debug')
command.append(self.config.appid)
os.system(' '.join(command))
--- a/server/session.py Wed May 05 18:10:07 2010 +0200
+++ b/server/session.py Wed May 05 18:10:33 2010 +0200
@@ -302,16 +302,15 @@
def set_language(self, language):
"""i18n configuration for translation"""
- vreg = self.vreg
language = language or self.user.property_value('ui.language')
try:
- gettext, pgettext = vreg.config.translations[language]
+ gettext, pgettext = self.vreg.config.translations[language]
self._ = self.__ = gettext
self.pgettext = pgettext
except KeyError:
- language = vreg.property_value('ui.language')
+ language = self.vreg.property_value('ui.language')
try:
- gettext, pgettext = vreg.config.translations[language]
+ gettext, pgettext = self.vreg.config.translations[language]
self._ = self.__ = gettext
self.pgettext = pgettext
except KeyError:
@@ -626,16 +625,6 @@
else:
del self.transaction_data['ecache'][eid]
- def base_url(self):
- url = self.repo.config['base-url']
- if not url:
- try:
- url = self.repo.config.default_base_url()
- except AttributeError: # default_base_url() might not be available
- self.warning('missing base-url definition in server config')
- url = u''
- return url
-
def from_controller(self):
"""return the id (string) of the controller issuing the request (no
sense here, always return 'view')
--- a/skeleton/data/external_resources.tmpl Wed May 05 18:10:07 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-# -*- shell-script -*-
-###############################################################################
-#
-# put here information about external resources used by your components,
-# or to overides existing external resources configuration
-#
-###############################################################################
-
-# CSS stylesheets to include in HTML headers
-# uncomment the line below to use template specific stylesheet
-# STYLESHEETS = DATADIR/cubes.%(cubename)s.css
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/skeleton/uiprops.py.tmpl Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,15 @@
+###############################################################################
+#
+# Put here information about external resources / styles used by your cube,
+# or to overides existing UI properties.
+#
+# Existing properties are available through the `sheet` dictionary available
+# in the global namespace. You also have access to a `data` function which
+# will return proper url for resources in the 'data' directory.
+#
+# /!\ this file should not be imported /!\
+###############################################################################
+
+# CSS stylesheets to include in HTML headers
+# uncomment the line below to use template specific stylesheet
+# STYLESHEETS = sheet['STYLESHEETS'] + [data('cubes.%(cubename)s.css')]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/scripts/script1.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,3 @@
+assert 'data/scripts/script1.py' == __file__
+assert '__main__' == __name__
+assert [] == __args__, __args__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/scripts/script2.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,3 @@
+assert 'data/scripts/script2.py' == __file__
+assert '__main__' == __name__
+assert ['-v'] == __args__, __args__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/scripts/script3.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,3 @@
+assert 'data/scripts/script3.py' == __file__
+assert '__main__' == __name__
+assert ['-vd', '-f', 'FILE.TXT'] == __args__, __args__
--- a/test/unittest_cwctl.py Wed May 05 18:10:07 2010 +0200
+++ b/test/unittest_cwctl.py Wed May 05 18:10:33 2010 +0200
@@ -24,8 +24,12 @@
from logilab.common.testlib import TestCase, unittest_main
from cubicweb.cwconfig import CubicWebConfiguration
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.server.migractions import ServerMigrationHelper
+
CubicWebConfiguration.load_cwctl_plugins() # XXX necessary?
+
class CubicWebCtlTC(TestCase):
def setUp(self):
self.stream = StringIO()
@@ -37,5 +41,25 @@
from cubicweb.cwctl import ListCommand
ListCommand().run([])
+
+class CubicWebShellTC(CubicWebTC):
+
+ def test_process_script_args_context(self):
+ repo = self.cnx._repo
+ mih = ServerMigrationHelper(None, repo=repo, cnx=self.cnx,
+ interactive=False,
+ # hack so it don't try to load fs schema
+ schema=1)
+ scripts = {'script1.py': list(),
+ 'script2.py': ['-v'],
+ 'script3.py': ['-vd', '-f', 'FILE.TXT'],
+ }
+ mih.cmd_process_script('data/scripts/script1.py', funcname=None)
+ for script, args in scripts.items():
+ scriptname = os.path.join('data/scripts/', script)
+ self.assert_(os.path.exists(scriptname))
+ mih.cmd_process_script(scriptname, None, scriptargs=args)
+
+
if __name__ == '__main__':
unittest_main()
--- a/test/unittest_vregistry.py Wed May 05 18:10:07 2010 +0200
+++ b/test/unittest_vregistry.py Wed May 05 18:10:33 2010 +0200
@@ -56,21 +56,25 @@
def test_load_subinterface_based_appobjects(self):
- self.vreg.reset()
self.vreg.register_objects([join(BASE, 'web', 'views', 'iprogress.py')])
# check progressbar was kicked
self.failIf(self.vreg['views'].get('progressbar'))
+ # we've to emulate register_objects to add custom MyCard objects
+ path = [join(BASE, 'entities', '__init__.py'),
+ join(BASE, 'web', 'views', 'iprogress.py')]
+ filemods = self.vreg.init_registration(path, None)
+ for filepath, modname in filemods:
+ self.vreg.load_file(filepath, modname)
class MyCard(Card):
__implements__ = (IMileStone,)
- self.vreg.reset()
self.vreg._loadedmods[__name__] = {}
self.vreg.register(MyCard)
- self.vreg.register_objects([join(BASE, 'entities', '__init__.py'),
- join(BASE, 'web', 'views', 'iprogress.py')])
+ self.vreg.initialization_completed()
# check progressbar isn't kicked
self.assertEquals(len(self.vreg['views']['progressbar']), 1)
def test_properties(self):
+ self.vreg.reset()
self.failIf('system.version.cubicweb' in self.vreg['propertydefs'])
self.failUnless(self.vreg.property_info('system.version.cubicweb'))
self.assertRaises(UnknownProperty, self.vreg.property_info, 'a.non.existent.key')
--- a/vregistry.py Wed May 05 18:10:07 2010 +0200
+++ b/vregistry.py Wed May 05 18:10:33 2010 +0200
@@ -406,6 +406,7 @@
# initialization methods ###################################################
def init_registration(self, path, extrapath=None):
+ self.reset()
# compute list of all modules that have to be loaded
self._toloadmods, filemods = _toload_info(path, extrapath)
# XXX is _loadedmods still necessary ? It seems like it's useful
--- a/web/application.py Wed May 05 18:10:07 2010 +0200
+++ b/web/application.py Wed May 05 18:10:33 2010 +0200
@@ -280,12 +280,12 @@
to publish HTTP request.
"""
- def __init__(self, config, debug=None,
+ def __init__(self, config,
session_handler_fact=CookieSessionHandler,
vreg=None):
self.info('starting web instance from %s', config.apphome)
if vreg is None:
- vreg = cwvreg.CubicWebVRegistry(config, debug=debug)
+ vreg = cwvreg.CubicWebVRegistry(config)
self.vreg = vreg
# connect to the repository and get instance's schema
self.repo = config.repository(vreg)
--- a/web/data/cubicweb.css Wed May 05 18:10:07 2010 +0200
+++ b/web/data/cubicweb.css Wed May 05 18:10:33 2010 +0200
@@ -3,14 +3,10 @@
* :copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
* :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
*/
-/***************************************/
-/* xhtml tags styles */
+
/***************************************/
-
-*{
- margin:0px;
- padding :0px;
-}
+/* xhtml tags */
+/***************************************/
html, body {
background: #e2e2e2;
@@ -78,7 +74,6 @@
}
table {
- border-collapse: collapse;
border: none;
}
@@ -97,7 +92,6 @@
ul{
margin: 1px 0px 1px 4px;
- list-style-type: none;
}
ul li {
@@ -138,10 +132,6 @@
font-weight: bold;
}
-iframe {
- border: 0px;
-}
-
pre {
font-family: Courier, "Courier New", Monaco, monospace;
font-size: 100%;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/cubicweb.reset.css Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,53 @@
+/* http://meyerweb.com/eric/tools/css/reset/ */
+/* v1.0 | 20080212 */
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%;
+ vertical-align: baseline;
+ background: transparent;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+
+/* remember to define focus styles! */
+:focus {
+ outline: 0;
+}
+
+/* remember to highlight inserts somehow! */
+ins {
+ text-decoration: none;
+}
+del {
+ text-decoration: line-through;
+}
+
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
--- a/web/data/cubicweb.timeline-bundle.js Wed May 05 18:10:07 2010 +0200
+++ b/web/data/cubicweb.timeline-bundle.js Wed May 05 18:10:33 2010 +0200
@@ -1,14 +1,14 @@
var SimileAjax_urlPrefix = baseuri() + 'data/';
var Timeline_urlPrefix = baseuri() + 'data/';
-/*==================================================
+/*
* Simile Ajax API
*
* Include this file in your HTML file as follows:
*
* <script src="http://simile.mit.edu/ajax/api/simile-ajax-api.js" type="text/javascript"></script>
*
- *==================================================
+ *
*/
if (typeof SimileAjax == "undefined") {
@@ -213,9 +213,9 @@
SimileAjax.loaded = true;
})();
}
-/*==================================================
+/*
* Platform Utility Functions and Constants
- *==================================================
+ *
*/
/* This must be called after our jQuery has been loaded
@@ -319,9 +319,10 @@
SimileAjax.Platform.getDefaultLocale = function() {
return SimileAjax.Platform.clientLocale;
-};/*==================================================
+};
+/*
* Debug Utility Functions
- *==================================================
+ *
*/
SimileAjax.Debug = {
@@ -678,9 +679,9 @@
}
};
})();
-/*==================================================
+/*
* DOM Utility Functions
- *==================================================
+ *
*/
SimileAjax.DOM = new Object();
@@ -1040,9 +1041,9 @@
SimileAjax.includeCssFile(document, SimileAjax.urlPrefix + "styles/graphics-ie6.css");
}
-/*==================================================
+/*
* Opacity, translucency
- *==================================================
+ *
*/
SimileAjax.Graphics._createTranslucentImage1 = function(url, verticalAlign) {
var elmt = document.createElement("img");
@@ -1119,9 +1120,9 @@
}
};
-/*==================================================
+/*
* Bubble
- *==================================================
+ *
*/
SimileAjax.Graphics.bubbleConfig = {
@@ -1479,9 +1480,9 @@
};
};
-/*==================================================
+/*
* Animation
- *==================================================
+ *
*/
/**
@@ -1549,11 +1550,11 @@
}
};
-/*==================================================
+/*
* CopyPasteButton
*
* Adapted from http://spaces.live.com/editorial/rayozzie/demo/liveclip/liveclipsample/techPreview.html.
- *==================================================
+ *
*/
/**
@@ -1606,9 +1607,9 @@
return div;
};
-/*==================================================
+/*
* getWidthHeight
- *==================================================
+ *
*/
SimileAjax.Graphics.getWidthHeight = function(el) {
// RETURNS hash {width: w, height: h} in pixels
@@ -1633,9 +1634,9 @@
};
-/*==================================================
+/*
* FontRenderingContext
- *==================================================
+ *
*/
SimileAjax.Graphics.getFontRenderingContext = function(elmt, width) {
return new SimileAjax.Graphics._FontRenderingContext(elmt, width);
@@ -2127,9 +2128,9 @@
var d = new Date().getTimezoneOffset();
return d / -60;
};
-/*==================================================
+/*
* String Utility Functions and Constants
- *==================================================
+ *
*/
String.prototype.trim = function() {
@@ -2170,9 +2171,9 @@
}
return result;
};
-/*==================================================
+/*
* HTML Utility Functions
- *==================================================
+ *
*/
SimileAjax.HTML = new Object();
@@ -2655,9 +2656,9 @@
return (this._a.length > 0) ? this._a[this._a.length - 1] : null;
};
-/*==================================================
+/*
* Event Index
- *==================================================
+ *
*/
SimileAjax.EventIndex = function(unit) {
@@ -2889,9 +2890,9 @@
return this._index < this._events.length() ?
this._events.elementAt(this._index++) : null;
}
-};/*==================================================
+};/*
* Default Unit
- *==================================================
+ *
*/
SimileAjax.NativeDateUnit = new Object();
@@ -2953,9 +2954,9 @@
return new Date(v.getTime() + n);
};
-/*==================================================
+/*
* General, miscellaneous SimileAjax stuff
- *==================================================
+ *
*/
SimileAjax.ListenerQueue = function(wildcardHandlerName) {
@@ -2998,7 +2999,7 @@
}
};
-/*======================================================================
+/*
* History
*
* This is a singleton that keeps track of undoable user actions and
@@ -3020,7 +3021,7 @@
*
* An iframe is inserted into the document's body element to track
* onload events.
- *======================================================================
+ *
*/
SimileAjax.History = {
@@ -3632,7 +3633,7 @@
}
return elmt;
};
-/*==================================================
+/*
* Timeline API
*
* This file will load all the Javascript files
@@ -3696,7 +3697,7 @@
* Note that the Ajax version is usually NOT the same as the Timeline version.
* See variable simile_ajax_ver below for the current version
*
- *==================================================
+ *
*/
(function() {
@@ -3928,7 +3929,7 @@
loadMe();
}
})();
-/*=================================================
+/*
*
* Coding standards:
*
@@ -3950,14 +3951,14 @@
* We also want to use jslint: http://www.jslint.com/
*
*
- *==================================================
+ *
*/
-/*==================================================
+/*
* Timeline VERSION
- *==================================================
+ *
*/
// Note: version is also stored in the build.xml file
Timeline.version = 'pre 2.4.0'; // use format 'pre 1.2.3' for trunk versions
@@ -3965,9 +3966,9 @@
Timeline.display_version = Timeline.version + ' (with Ajax lib ' + Timeline.ajax_lib_version + ')';
// cf method Timeline.writeVersion
-/*==================================================
+/*
* Timeline
- *==================================================
+ *
*/
Timeline.strings = {}; // localization string tables
Timeline.HORIZONTAL = 0;
@@ -4183,9 +4184,9 @@
-/*==================================================
+/*
* Timeline Implementation object
- *==================================================
+ *
*/
Timeline._Impl = function(elmt, bandInfos, orientation, unit, timelineID) {
SimileAjax.WindowManager.initialize();
@@ -4585,7 +4586,7 @@
this.paint();
};
-/*=================================================
+/*
*
* Coding standards:
*
@@ -4607,14 +4608,14 @@
* We also want to use jslint: http://www.jslint.com/
*
*
- *==================================================
+ *
*/
-/*==================================================
+/*
* Band
- *==================================================
+ *
*/
Timeline._Band = function(timeline, bandInfo, index) {
// hack for easier subclassing
@@ -5344,9 +5345,9 @@
Timeline._Band.prototype.closeBubble = function() {
SimileAjax.WindowManager.cancelPopups();
};
-/*==================================================
+/*
* Classic Theme
- *==================================================
+ *
*/
@@ -5523,14 +5524,14 @@
};
this.mouseWheel = 'scroll'; // 'default', 'zoom', 'scroll'
-};/*==================================================
+};/*
* An "ether" is a object that maps date/time to pixel coordinates.
- *==================================================
+ *
*/
-/*==================================================
+/*
* Linear Ether
- *==================================================
+ *
*/
Timeline.LinearEther = function(params) {
@@ -5601,9 +5602,9 @@
};
-/*==================================================
+/*
* Hot Zone Ether
- *==================================================
+ *
*/
Timeline.HotZoneEther = function(params) {
@@ -5828,9 +5829,9 @@
Timeline.HotZoneEther.prototype._getScale = function() {
return this._interval / this._pixelsPerInterval;
};
-/*==================================================
+/*
* Gregorian Ether Painter
- *==================================================
+ *
*/
Timeline.GregorianEtherPainter = function(params) {
@@ -5919,9 +5920,9 @@
};
-/*==================================================
+/*
* Hot Zone Gregorian Ether Painter
- *==================================================
+ *
*/
Timeline.HotZoneGregorianEtherPainter = function(params) {
@@ -6080,9 +6081,9 @@
}
};
-/*==================================================
+/*
* Year Count Ether Painter
- *==================================================
+ *
*/
Timeline.YearCountEtherPainter = function(params) {
@@ -6169,9 +6170,9 @@
Timeline.YearCountEtherPainter.prototype.softPaint = function() {
};
-/*==================================================
+/*
* Quarterly Ether Painter
- *==================================================
+ *
*/
Timeline.QuarterlyEtherPainter = function(params) {
@@ -6257,9 +6258,9 @@
Timeline.QuarterlyEtherPainter.prototype.softPaint = function() {
};
-/*==================================================
+/*
* Ether Interval Marker Layout
- *==================================================
+ *
*/
Timeline.EtherIntervalMarkerLayout = function(timeline, band, theme, align, showLine) {
@@ -6363,9 +6364,9 @@
};
};
-/*==================================================
+/*
* Ether Highlight Layout
- *==================================================
+ *
*/
Timeline.EtherHighlight = function(timeline, band, theme, backgroundLayer) {
@@ -6404,9 +6405,9 @@
}
}
};
-/*==================================================
+/*
* Event Utils
- *==================================================
+ *
*/
Timeline.EventUtils = {};
@@ -6421,7 +6422,7 @@
};
Timeline.EventUtils.decodeEventElID = function(elementID) {
- /*==================================================
+ /*
*
* Use this function to decode an event element's id on a band (label div,
* tape div or icon img).
@@ -6447,7 +6448,7 @@
* by using Timeline.getTimeline, Timeline.getBand, or
* Timeline.getEvent and passing in the element's id
*
- *==================================================
+ *
*/
var parts = elementID.split('-');
@@ -6467,9 +6468,9 @@
// elType should be one of {label | icon | tapeN | highlightN}
return elType + "-tl-" + timeline.timelineID +
"-" + band.getIndex() + "-" + evt.getID();
-};/*==================================================
+};/*
* Gregorian Date Labeller
- *==================================================
+ *
*/
Timeline.GregorianDateLabeller = function(locale, timeZone) {
@@ -6558,9 +6559,9 @@
return { text: text, emphasized: emphasized };
}
-/*==================================================
+/*
* Default Event Source
- *==================================================
+ *
*/
@@ -7125,12 +7126,12 @@
};
-/*==================================================
+/*
* Original Event Painter
- *==================================================
+ *
*/
-/*==================================================
+/*
*
* To enable a single event listener to monitor everything
* on a Timeline, we need a way to map from an event's icon,
@@ -7152,7 +7153,7 @@
* You can then retrieve the band/timeline objects and event object
* by using Timeline.EventUtils.decodeEventElID
*
- *==================================================
+ *
*/
/*
@@ -7818,9 +7819,9 @@
this._eventPaintListeners[i](this._band, op, evt, els);
}
};
-/*==================================================
+/*
* Detailed Event Painter
- *==================================================
+ *
*/
// Note: a number of features from original-painter
@@ -8509,9 +8510,9 @@
this._onSelectListeners[i](eventID);
}
};
-/*==================================================
+/*
* Overview Event Painter
- *==================================================
+ *
*/
Timeline.OverviewEventPainter = function(params) {
@@ -8767,9 +8768,9 @@
Timeline.OverviewEventPainter.prototype.showBubble = function(evt) {
// not implemented
};
-/*==================================================
+/*
* Compact Event Painter
- *==================================================
+ *
*/
Timeline.CompactEventPainter = function(params) {
@@ -9831,9 +9832,9 @@
this._onSelectListeners[i](eventIDs);
}
};
-/*==================================================
+/*
* Span Highlight Decorator
- *==================================================
+ *
*/
Timeline.SpanHighlightDecorator = function(params) {
@@ -9948,9 +9949,9 @@
Timeline.SpanHighlightDecorator.prototype.softPaint = function() {
};
-/*==================================================
+/*
* Point Highlight Decorator
- *==================================================
+ *
*/
Timeline.PointHighlightDecorator = function(params) {
@@ -10015,9 +10016,9 @@
Timeline.PointHighlightDecorator.prototype.softPaint = function() {
};
-/*==================================================
+/*
* Default Unit
- *==================================================
+ *
*/
Timeline.NativeDateUnit = new Object();
@@ -10083,35 +10084,35 @@
return new Date(v.getTime() + n);
};
-/*==================================================
+/*
* Common localization strings
- *==================================================
+ *
*/
Timeline.strings["fr"] = {
wikiLinkLabel: "Discute"
};
-/*==================================================
+/*
* Localization of labellers.js
- *==================================================
+ *
*/
Timeline.GregorianDateLabeller.monthNames["fr"] = [
"jan", "fev", "mar", "avr", "mai", "jui", "jui", "aou", "sep", "oct", "nov", "dec"
];
-/*==================================================
+/*
* Common localization strings
- *==================================================
+ *
*/
Timeline.strings["en"] = {
wikiLinkLabel: "Discuss"
};
-/*==================================================
+/*
* Localization of labellers.js
- *==================================================
+ *
*/
Timeline.GregorianDateLabeller.monthNames["en"] = [
--- a/web/data/cubicweb.widgets.js Wed May 05 18:10:07 2010 +0200
+++ b/web/data/cubicweb.widgets.js Wed May 05 18:10:33 2010 +0200
@@ -313,34 +313,5 @@
});
-/*
- * ComboBox with a textinput : allows to add a new value
- */
-
-Widgets.AddComboBox = defclass('AddComboBox', null, {
- __init__ : function(wdgnode) {
- jQuery("#add_newopt").click(function() {
- var new_val = jQuery("#newopt").val();
- if (!new_val){
- return false;
- }
- name = wdgnode.getAttribute('name').split(':');
- this.rel = name[0];
- this.eid_to = name[1];
- this.etype_to = wdgnode.getAttribute('cubicweb:etype_to');
- this.etype_from = wdgnode.getAttribute('cubicweb:etype_from');
- var d = asyncRemoteExec('add_and_link_new_entity', this.etype_to, this.rel, this.eid_to, this.etype_from, 'new_val');
- d.addCallback(function (eid) {
- jQuery(wdgnode).find("option[selected]").removeAttr("selected");
- var new_option = OPTION({'value':eid, 'selected':'selected'}, value=new_val);
- wdgnode.appendChild(new_option);
- });
- d.addErrback(function (xxx) {
- log('xxx =', xxx);
- });
- });
- }
-});
-
CubicWeb.provide('widgets.js');
--- a/web/data/external_resources Wed May 05 18:10:07 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-# -*- shell-script -*-
-###############################################################################
-#
-# external resources file for core library resources
-#
-# Commented values are default values used by the application.
-#
-###############################################################################
-
-
-# CSS stylesheets to include in HTML headers
-#STYLESHEETS = DATADIR/cubicweb.css
-
-# CSS stylesheets for print
-#STYLESHEETS_PRINT = DATADIR/cubicweb.print.css
-
-#CSS stylesheets for IE
-#IE_STYLESHEETS = DATADIR/cubicweb.ie.css
-
-# Javascripts files to include in HTML headers
-#JAVASCRIPTS = DATADIR/jquery.js, DATADIR/cubicweb.python.js, DATADIR/jquery.json.js, DATADIR/cubicweb.compat.js, DATADIR/cubicweb.htmlhelpers.js
-
-# path to favicon (relative to the application main script, seen as a
-# directory, hence .. when you are not using an absolute path)
-#FAVICON = DATADIR/favicon.ico
-
-# path to the logo (relative to the application main script, seen as a
-# directory, hence .. when you are not using an absolute path)
-LOGO = DATADIR/logo.png
-
-# rss logo (link to get the rss view of a selection)
-RSS_LOGO = DATADIR/rss.png
-RSS_LOGO_16 = DATADIR/feed-icon16x16.png
-RSS_LOGO_32 = DATADIR/feed-icon32x32.png
-
-# path to search image
-SEARCH_GO = DATADIR/go.png
-
-#FCKEDITOR_PATH = /usr/share/fckeditor/
-
-PUCE_UP = DATADIR/puce_up.png
-PUCE_DOWN = DATADIR/puce_down.png
-
-# icons for entity types
-BOOKMARK_ICON = DATADIR/icon_bookmark.gif
-EMAILADDRESS_ICON = DATADIR/icon_emailaddress.gif
-EUSER_ICON = DATADIR/icon_euser.gif
-STATE_ICON = DATADIR/icon_state.gif
-
-# other icons
-CALENDAR_ICON = DATADIR/calendar.gif
-CANCEL_EMAIL_ICON = DATADIR/sendcancel.png
-SEND_EMAIL_ICON = DATADIR/sendok.png
-DOWNLOAD_ICON = DATADIR/download.gif
-UPLOAD_ICON = DATADIR/upload.gif
-GMARKER_ICON = DATADIR/gmap_blue_marker.png
-UP_ICON = DATADIR/up.gif
-
-OK_ICON = DATADIR/ok.png
-CANCEL_ICON = DATADIR/cancel.png
-APPLY_ICON = DATADIR/plus.png
-TRASH_ICON = DATADIR/trash_can_small.png
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/uiprops.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,57 @@
+"""define default ui properties"""
+
+# CSS stylesheets to include systematically in HTML headers
+STYLESHEETS = [data('cubicweb.reset.css'),
+ data('cubicweb.css')]
+STYLESHEETS_IE = [data('cubicweb.ie.css')]
+STYLESHEETS_PRINT = [data('cubicweb.print.css')]
+
+# Javascripts files to include systematically in HTML headers
+JAVASCRIPTS = [data('jquery.js'),
+ data('jquery.corner.js'),
+ data('jquery.json.js'),
+ data('cubicweb.compat.js'),
+ data('cubicweb.python.js'),
+ data('cubicweb.htmlhelpers.js')]
+
+# where is installed fckeditor
+FCKEDITOR_PATH = '/usr/share/fckeditor/'
+
+# favicon and logo for the instance
+FAVICON = data('favicon.ico')
+LOGO = data('logo.png')
+
+# rss logo (link to get the rss view of a selection)
+RSS_LOGO = data('rss.png')
+RSS_LOGO_16 = data('feed-icon16x16.png')
+RSS_LOGO_32 = data('feed-icon32x32.png')
+
+# XXX cleanup resources below, some of them are probably not used
+# (at least entity types icons...)
+
+# images
+HELP = data('help.png')
+SEARCH_GO = data('go.png')
+PUCE_UP = data('puce_up.png')
+PUCE_DOWN = data('puce_down.png')
+
+# button icons
+OK_ICON = data('ok.png')
+CANCEL_ICON = data('cancel.png')
+APPLY_ICON = data('plus.png')
+TRASH_ICON = data('trash_can_small.png')
+
+# icons for entity types
+BOOKMARK_ICON = data('icon_bookmark.gif')
+EMAILADDRESS_ICON = data('icon_emailaddress.gif')
+EUSER_ICON = data('icon_euser.gif')
+STATE_ICON = data('icon_state.gif')
+
+# other icons
+CALENDAR_ICON = data('calendar.gif')
+CANCEL_EMAIL_ICON = data('sendcancel.png')
+SEND_EMAIL_ICON = data('sendok.png')
+DOWNLOAD_ICON = data('download.gif')
+UPLOAD_ICON = data('upload.gif')
+GMARKER_ICON = data('gmap_blue_marker.png')
+UP_ICON = data('up.gif')
--- a/web/formwidgets.py Wed May 05 18:10:07 2010 +0200
+++ b/web/formwidgets.py Wed May 05 18:10:33 2010 +0200
@@ -60,7 +60,6 @@
.. autoclass:: cubicweb.web.formwidgets.AjaxWidget
.. autoclass:: cubicweb.web.formwidgets.AutoCompletionWidget
-.. kill or document AddComboBoxWidget
.. kill or document StaticFileAutoCompletionWidget
.. kill or document LazyRestrictedAutoCompletionWidget
.. kill or document RestrictedAutoCompletionWidget
@@ -550,7 +549,7 @@
return (u"""<a onclick="toggleCalendar('%s', '%s', %s, %s);" class="calhelper">
<img src="%s" title="%s" alt="" /></a><div class="calpopup hidden" id="%s"></div>"""
% (helperid, inputid, year, month,
- form._cw.external_resource('CALENDAR_ICON'),
+ form._cw.uiprops['CALENDAR_ICON'],
form._cw._('calendar'), helperid) )
@@ -574,7 +573,7 @@
req.add_onload(u'jqNode("%s").datepicker('
'{buttonImage: "%s", dateFormat: "%s", firstDay: 1,'
' showOn: "button", buttonImageOnly: true})' % (
- domid, req.external_resource('CALENDAR_ICON'), fmt))
+ domid, req.uiprops['CALENDAR_ICON'], fmt))
if self.datestr is None:
value = self.values(form, field)[0]
else:
@@ -776,24 +775,6 @@
return entity.view('combobox')
-class AddComboBoxWidget(Select):
- def attributes(self, form, field):
- attrs = super(AddComboBoxWidget, self).attributes(form, field)
- init_ajax_attributes(attrs, 'AddComboBox')
- # XXX entity form specific
- entity = form.edited_entity
- attrs['cubicweb:etype_to'] = entity.e_schema
- etype_from = entity.e_schema.subjrels[field.name].objects(entity.e_schema)[0]
- attrs['cubicweb:etype_from'] = etype_from
- return attrs
-
- def _render(self, form, field, renderer):
- return super(AddComboBoxWidget, self)._render(form, field, renderer) + u'''
-<div id="newvalue">
- <input type="text" id="newopt" />
- <a href="javascript:noop()" id="add_newopt"> </a></div>
-'''
-
# more widgets #################################################################
class IntervalWidget(FieldWidget):
@@ -954,7 +935,7 @@
if self.settabindex and not 'tabindex' in attrs:
attrs['tabindex'] = form._cw.next_tabindex()
if self.icon:
- img = tags.img(src=form._cw.external_resource(self.icon), alt=self.icon)
+ img = tags.img(src=form._cw.uiprops[self.icon], alt=self.icon)
else:
img = u''
return tags.button(img + xml_escape(label), escapecontent=False,
@@ -985,7 +966,7 @@
def render(self, form, field=None, renderer=None):
label = form._cw._(self.label)
- imgsrc = form._cw.external_resource(self.imgressource)
+ imgsrc = form._cw.uiprops[self.imgressource]
return '<a id="%(domid)s" href="%(href)s">'\
'<img src="%(imgsrc)s" alt="%(label)s"/>%(label)s</a>' % {
'label': label, 'imgsrc': imgsrc,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/propertysheet.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,100 @@
+# copyright 2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
+"""property sheets allowing configuration of the web ui"""
+
+__docformat__ = "restructuredtext en"
+
+import re
+import os
+import os.path as osp
+
+
+class PropertySheet(dict):
+ def __init__(self, cache_directory, **context):
+ self._cache_directory = cache_directory
+ self.context = context
+ self.reset()
+ context['sheet'] = self
+ self._percent_rgx = re.compile('%(?!\()')
+
+ def reset(self):
+ self.clear()
+ self._ordered_propfiles = []
+ self._propfile_mtime = {}
+ self._sourcefile_mtime = {}
+ self._cache = {}
+
+ def load(self, fpath):
+ scriptglobals = self.context.copy()
+ scriptglobals['__file__'] = fpath
+ execfile(fpath, scriptglobals, self)
+ self._propfile_mtime[fpath] = os.stat(fpath)[-2]
+ self._ordered_propfiles.append(fpath)
+
+ def need_reload(self):
+ for rid, (adirectory, rdirectory, mtime) in self._cache.items():
+ if os.stat(osp.join(rdirectory, rid))[-2] > mtime:
+ del self._cache[rid]
+ for fpath, mtime in self._propfile_mtime.iteritems():
+ if os.stat(fpath)[-2] > mtime:
+ return True
+ return False
+
+ def reload(self):
+ ordered_files = self._ordered_propfiles
+ self.reset()
+ for fpath in ordered_files:
+ self.load(fpath)
+
+ def reload_if_needed(self):
+ if self.need_reload():
+ self.reload()
+
+ def process_resource(self, rdirectory, rid):
+ try:
+ return self._cache[rid][0]
+ except KeyError:
+ cachefile = osp.join(self._cache_directory, rid)
+ self.debug('caching processed %s/%s into %s',
+ rdirectory, rid, cachefile)
+ rcachedir = osp.dirname(cachefile)
+ if not osp.exists(rcachedir):
+ os.makedirs(rcachedir)
+ sourcefile = osp.join(rdirectory, rid)
+ content = file(sourcefile).read()
+ # XXX replace % not followed by a paren by %% to avoid having to do
+ # this in the source css file ?
+ try:
+ content = self.compile(content)
+ except ValueError, ex:
+ self.error("can't process %s/%s: %s", rdirectory, rid, ex)
+ adirectory = rdirectory
+ else:
+ stream = file(cachefile, 'w')
+ stream.write(content)
+ stream.close()
+ adirectory = self._cache_directory
+ self._cache[rid] = (adirectory, rdirectory, os.stat(sourcefile)[-2])
+ return adirectory
+
+ def compile(self, content):
+ return self._percent_rgx.sub('%%', content) % self
+
+from cubicweb.web import LOGGER
+from logilab.common.logging_ext import set_log_methods
+set_log_methods(PropertySheet, LOGGER)
--- a/web/request.py Wed May 05 18:10:07 2010 +0200
+++ b/web/request.py Wed May 05 18:10:33 2010 +0200
@@ -83,6 +83,12 @@
super(CubicWebRequestBase, self).__init__(vreg)
self.authmode = vreg.config['auth-mode']
self.https = https
+ if https:
+ self.uiprops = vreg.config.https_uiprops
+ self.datadir_url = vreg.config.https_datadir_url
+ else:
+ self.uiprops = vreg.config.uiprops
+ self.datadir_url = vreg.config.datadir_url
# raw html headers that can be added from any view
self.html_headers = HTMLHead()
# form parameters
@@ -99,7 +105,6 @@
self.next_tabindex = self.tabindexgen.next
# page id, set by htmlheader template
self.pageid = None
- self.datadir_url = self._datadir_url()
self._set_pageid()
# prepare output header
self.headers_out = Headers()
@@ -589,10 +594,6 @@
"""return currently accessed url"""
return self.base_url() + self.relative_path(includeparams)
- def _datadir_url(self):
- """return url of the instance's data directory"""
- return self.base_url() + 'data%s/' % self.vreg.config.instance_md5_version()
-
def selected(self, url):
"""return True if the url is equivalent to currently accessed url"""
reqpath = self.relative_path().lower()
@@ -618,25 +619,6 @@
return controller
return 'view'
- def external_resource(self, rid, default=_MARKER):
- """return a path to an external resource, using its identifier
-
- raise KeyError if the resource is not defined
- """
- try:
- value = self.vreg.config.ext_resources[rid]
- except KeyError:
- if default is _MARKER:
- raise
- return default
- if value is None:
- return None
- baseurl = self.datadir_url[:-1] # remove trailing /
- if isinstance(value, list):
- return [v.replace('DATADIR', baseurl) for v in value]
- return value.replace('DATADIR', baseurl)
- external_resource = cached(external_resource, keyarg=1)
-
def validate_cache(self):
"""raise a `DirectResponse` exception if a cached page along the way
exists and is still usable.
@@ -712,12 +694,6 @@
auth, ex.__class__.__name__, ex)
return None, None
- @deprecated("[3.4] use parse_accept_header('Accept-Language')")
- def header_accept_language(self):
- """returns an ordered list of preferred languages"""
- return [value.split('-')[0] for value in
- self.parse_accept_header('Accept-Language')]
-
def parse_accept_header(self, header):
"""returns an ordered list of preferred languages"""
accepteds = self.get_header(header, '')
@@ -823,5 +799,25 @@
u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">')
return u'<div>'
+ @deprecated('[3.9] use req.uiprops[rid]')
+ def external_resource(self, rid, default=_MARKER):
+ """return a path to an external resource, using its identifier
+
+ raise `KeyError` if the resource is not defined
+ """
+ try:
+ return self.uiprops[rid]
+ except KeyError:
+ if default is _MARKER:
+ raise
+ return default
+
+ @deprecated("[3.4] use parse_accept_header('Accept-Language')")
+ def header_accept_language(self):
+ """returns an ordered list of preferred languages"""
+ return [value.split('-')[0] for value in
+ self.parse_accept_header('Accept-Language')]
+
+
from cubicweb import set_log_methods
set_log_methods(CubicWebRequestBase, LOGGER)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/data/pouet.css Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,3 @@
+body { background-color: %(bgcolor)s
+ font-size: 100%;
+ }
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/data/sheet1.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,3 @@
+bgcolor = '#000000'
+stylesheets = ['%s/cubicweb.css' % datadir_url]
+logo = '%s/logo.png' % datadir_url
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/data/sheet2.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,3 @@
+fontcolor = 'black'
+bgcolor = '#FFFFFF'
+stylesheets = sheet['stylesheets'] + ['%s/mycube.css' % datadir_url]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_propertysheet.py Wed May 05 18:10:33 2010 +0200
@@ -0,0 +1,49 @@
+import os
+from os.path import join, dirname
+from shutil import rmtree
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb.web.propertysheet import *
+
+DATADIR = join(dirname(__file__), 'data')
+CACHEDIR = join(DATADIR, 'uicache')
+
+class PropertySheetTC(TestCase):
+
+ def tearDown(self):
+ rmtree(CACHEDIR)
+
+ def test(self):
+ ps = PropertySheet(CACHEDIR, datadir_url='http://cwtest.com')
+ ps.load(join(DATADIR, 'sheet1.py'))
+ ps.load(join(DATADIR, 'sheet2.py'))
+ # defined by sheet1
+ self.assertEquals(ps['logo'], 'http://cwtest.com/logo.png')
+ # defined by sheet1, overriden by sheet2
+ self.assertEquals(ps['bgcolor'], '#FFFFFF')
+ # defined by sheet2
+ self.assertEquals(ps['fontcolor'], 'black')
+ # defined by sheet1, extended by sheet2
+ self.assertEquals(ps['stylesheets'], ['http://cwtest.com/cubicweb.css',
+ 'http://cwtest.com/mycube.css'])
+ self.assertEquals(ps.compile('a {bgcolor: %(bgcolor)s; size: 1%;}'),
+ 'a {bgcolor: #FFFFFF; size: 1%;}')
+ self.assertEquals(ps.process_resource(DATADIR, 'pouet.css'),
+ CACHEDIR)
+ self.failUnless('pouet.css' in ps._cache)
+ self.failIf(ps.need_reload())
+ os.utime(join(DATADIR, 'sheet1.py'), None)
+ self.failUnless('pouet.css' in ps._cache)
+ self.failUnless(ps.need_reload())
+ self.failUnless('pouet.css' in ps._cache)
+ ps.reload()
+ self.failIf('pouet.css' in ps._cache)
+ self.failIf(ps.need_reload())
+ ps.process_resource(DATADIR, 'pouet.css') # put in cache
+ os.utime(join(DATADIR, 'pouet.css'), None)
+ self.failIf(ps.need_reload())
+ self.failIf('pouet.css' in ps._cache)
+
+if __name__ == '__main__':
+ unittest_main()
--- a/web/test/unittest_views_basecontrollers.py Wed May 05 18:10:07 2010 +0200
+++ b/web/test/unittest_views_basecontrollers.py Wed May 05 18:10:33 2010 +0200
@@ -643,7 +643,7 @@
# silly tests
def test_external_resource(self):
self.assertEquals(self.remote_call('external_resource', 'RSS_LOGO')[0],
- json.dumps(self.request().external_resource('RSS_LOGO')))
+ json.dumps(self.config.uiprops['RSS_LOGO']))
def test_i18n(self):
self.assertEquals(self.remote_call('i18n', ['bimboom'])[0],
json.dumps(['bimboom']))
--- a/web/test/unittest_webconfig.py Wed May 05 18:10:07 2010 +0200
+++ b/web/test/unittest_webconfig.py Wed May 05 18:10:33 2010 +0200
@@ -33,15 +33,14 @@
def test_nonregr_print_css_as_list(self):
"""make sure PRINT_CSS *must* is a list"""
config = self.config
- req = fake.FakeRequest()
- print_css = req.external_resource('STYLESHEETS_PRINT')
+ print_css = config.uiprops['STYLESHEETS_PRINT']
self.failUnless(isinstance(print_css, list))
- ie_css = req.external_resource('IE_STYLESHEETS')
+ ie_css = config.uiprops['STYLESHEETS_IE']
self.failUnless(isinstance(ie_css, list))
def test_locate_resource(self):
- self.failUnless('FILE_ICON' in self.config.ext_resources)
- rname = self.config.ext_resources['FILE_ICON'].replace('DATADIR/', '')
+ self.failUnless('FILE_ICON' in self.config.uiprops)
+ rname = self.config.uiprops['FILE_ICON'].replace(self.config.datadir_url, '')
self.failUnless('file' in self.config.locate_resource(rname).split(os.sep))
cubicwebcsspath = self.config.locate_resource('cubicweb.css').split(os.sep)
self.failUnless('web' in cubicwebcsspath or 'shared' in cubicwebcsspath) # 'shared' if tests under apycot
--- a/web/views/basecomponents.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/basecomponents.py Wed May 05 18:10:33 2010 +0200
@@ -79,7 +79,7 @@
def call(self):
self.w(u'<a href="%s"><img class="logo" src="%s" alt="logo"/></a>'
- % (self._cw.base_url(), self._cw.external_resource('LOGO')))
+ % (self._cw.base_url(), self._cw.uiprops['LOGO']))
class ApplHelp(component.Component):
--- a/web/views/basecontrollers.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/basecontrollers.py Wed May 05 18:10:33 2010 +0200
@@ -340,12 +340,11 @@
return None
return None
- def _call_view(self, view, **kwargs):
- req = self._cw
- divid = req.form.get('divid', 'pageContent')
+ def _call_view(self, view, paginate=False, **kwargs):
+ divid = self._cw.form.get('divid', 'pageContent')
# we need to call pagination before with the stream set
stream = view.set_stream()
- if req.form.get('paginate'):
+ if paginate:
if divid == 'pageContent':
# mimick main template behaviour
stream.write(u'<div id="pageContent">')
@@ -356,12 +355,12 @@
if divid == 'pageContent':
stream.write(u'<div id="contentmain">')
view.render(**kwargs)
- extresources = req.html_headers.getvalue(skiphead=True)
+ extresources = self._cw.html_headers.getvalue(skiphead=True)
if extresources:
stream.write(u'<div class="ajaxHtmlHead">\n') # XXX use a widget ?
stream.write(extresources)
stream.write(u'</div>\n')
- if req.form.get('paginate') and divid == 'pageContent':
+ if paginate and divid == 'pageContent':
stream.write(u'</div></div>')
return stream.getvalue()
@@ -381,7 +380,7 @@
vid = req.form.get('fallbackvid', 'noresult')
view = self._cw.vreg['views'].select(vid, req, rset=rset)
self.validate_cache(view)
- return self._call_view(view)
+ return self._call_view(view, paginate=req.form.get('paginate'))
@xhtmlize
def js_prop_widget(self, propkey, varname, tabindex=None):
@@ -419,16 +418,7 @@
**extraargs)
#except NoSelectableObject:
# raise RemoteCallFailed('unselectable')
- extraargs = extraargs or {}
- stream = comp.set_stream()
- comp.render(**extraargs)
- # XXX why not _call_view ?
- extresources = self._cw.html_headers.getvalue(skiphead=True)
- if extresources:
- stream.write(u'<div class="ajaxHtmlHead">\n')
- stream.write(extresources)
- stream.write(u'</div>\n')
- return stream.getvalue()
+ return self._call_view(comp, **extraargs)
@check_pageid
@xhtmlize
@@ -457,15 +447,7 @@
args['reload'] = json.loads(args['reload'])
rset = req.eid_rset(int(self._cw.form['eid']))
view = req.vreg['views'].select('doreledit', req, rset=rset, rtype=args['rtype'])
- stream = view.set_stream()
- view.render(**args)
- # XXX why not _call_view ?
- extresources = req.html_headers.getvalue(skiphead=True)
- if extresources:
- stream.write(u'<div class="ajaxHtmlHead">\n')
- stream.write(extresources)
- stream.write(u'</div>\n')
- return stream.getvalue()
+ return self._call_view(view, **args)
@jsonize
def js_i18n(self, msgids):
@@ -481,7 +463,7 @@
@jsonize
def js_external_resource(self, resource):
"""returns the URL of the external resource named `resource`"""
- return self._cw.external_resource(resource)
+ return self._cw.uiprops[resource]
@check_pageid
@jsonize
@@ -581,14 +563,6 @@
def js_add_pending_delete(self, (eidfrom, rel, eidto)):
self._add_pending(eidfrom, rel, eidto, 'delete')
- # XXX specific code. Kill me and my AddComboBox friend
- @jsonize
- def js_add_and_link_new_entity(self, etype_to, rel, eid_to, etype_from, value_from):
- # create a new entity
- eid_from = self._cw.execute('INSERT %s T : T name "%s"' % ( etype_from, value_from ))[0][0]
- # link the new entity to the main entity
- rql = 'SET F %(rel)s T WHERE F eid %(eid_to)s, T eid %(eid_from)s' % {'rel' : rel, 'eid_to' : eid_to, 'eid_from' : eid_from}
- return eid_from
# XXX move to massmailing
class SendMailController(Controller):
--- a/web/views/basetemplates.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/basetemplates.py Wed May 05 18:10:33 2010 +0200
@@ -294,22 +294,22 @@
self.alternates()
def favicon(self):
- favicon = self._cw.external_resource('FAVICON', None)
+ favicon = self._cw.uiprops.get('FAVICON', None)
if favicon:
self.whead(u'<link rel="shortcut icon" href="%s"/>\n' % favicon)
def stylesheets(self):
req = self._cw
add_css = req.add_css
- for css in req.external_resource('STYLESHEETS'):
+ for css in req.uiprops['STYLESHEETS']:
add_css(css, localfile=False)
- for css in req.external_resource('STYLESHEETS_PRINT'):
+ for css in req.uiprops['STYLESHEETS_PRINT']:
add_css(css, u'print', localfile=False)
- for css in req.external_resource('IE_STYLESHEETS'):
+ for css in req.uiprops['STYLESHEETS_IE']:
add_css(css, localfile=False, ieonly=True)
def javascripts(self):
- for jscript in self._cw.external_resource('JAVASCRIPTS'):
+ for jscript in self._cw.uiprops['JAVASCRIPTS']:
self._cw.add_js(jscript, localfile=False)
def alternates(self):
--- a/web/views/idownloadable.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/idownloadable.py Wed May 05 18:10:33 2010 +0200
@@ -48,7 +48,7 @@
w(u'<div class="sideBox downloadBox"><div class="sideBoxBody">')
w(u'<a href="%s"><img src="%s" alt="%s"/> %s</a>'
% (xml_escape(entity.download_url()),
- req.external_resource('DOWNLOAD_ICON'),
+ req.uiprops['DOWNLOAD_ICON'],
_('download icon'), xml_escape(label or entity.dc_title())))
w(u'%s</div>' % footer)
w(u'</div></div>\n')
--- a/web/views/igeocodable.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/igeocodable.py Wed May 05 18:10:33 2010 +0200
@@ -59,7 +59,7 @@
if hasattr(entity, 'marker_icon'):
icon = entity.marker_icon()
else:
- icon = (self._cw.external_resource('GMARKER_ICON'), (20, 34), (4, 34), None)
+ icon = (self._cw.uiprops['GMARKER_ICON'], (20, 34), (4, 34), None)
return {'latitude': entity.latitude, 'longitude': entity.longitude,
'title': entity.dc_long_title(),
#icon defines : (icon._url, icon.size, icon.iconAncho', icon.shadow)
--- a/web/views/management.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/management.py Wed May 05 18:10:33 2010 +0200
@@ -205,7 +205,7 @@
cversions = []
for cube in self._cw.vreg.config.cubes():
cubeversion = vcconf.get(cube, self._cw._('no version information'))
- w(u"<b>Package %s version:</b> %s<br/>\n" % (cube, cubeversion))
+ w(u"<b>Cube %s version:</b> %s<br/>\n" % (cube, cubeversion))
cversions.append((cube, cubeversion))
w(u"</div>")
# creates a bug submission link if submit-mail is set
@@ -239,7 +239,7 @@
binfo += u'\n'.join(u' * %s = %s' % (k, v) for k, v in req.form.iteritems())
binfo += u'\n\n:CubicWeb version: %s\n' % (eversion,)
for pkg, pkgversion in cubes:
- binfo += u":Package %s version: %s\n" % (pkg, pkgversion)
+ binfo += u":Cube %s version: %s\n" % (pkg, pkgversion)
binfo += '\n'
return binfo
--- a/web/views/schema.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/schema.py Wed May 05 18:10:33 2010 +0200
@@ -248,7 +248,7 @@
eschema.type, self._cw.build_url('cwetype/%s' % eschema.type),
eschema.type, _(eschema.type)))
self.w(u'<a href="%s#schema_security"><img src="%s" alt="%s"/></a>' % (
- url, self._cw.external_resource('UP_ICON'), _('up')))
+ url, self._cw.uiprops['UP_ICON'], _('up')))
self.w(u'</h3>')
self.w(u'<div style="margin: 0px 1.5em">')
self.permissions_table(eschema)
@@ -277,7 +277,7 @@
rschema.type, self._cw.build_url('cwrtype/%s' % rschema.type),
rschema.type, _(rschema.type)))
self.w(u'<a href="%s#schema_security"><img src="%s" alt="%s"/></a>' % (
- url, self._cw.external_resource('UP_ICON'), _('up')))
+ url, self._cw.uiprops['UP_ICON'], _('up')))
self.w(u'</h3>')
self.grouped_permissions_table(rschema)
--- a/web/views/tableview.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/tableview.py Wed May 05 18:10:33 2010 +0200
@@ -204,7 +204,7 @@
def render_actions(self, divid, actions):
box = MenuWidget('', 'tableActionsBox', _class='', islist=False)
- label = tags.img(src=self._cw.external_resource('PUCE_DOWN'),
+ label = tags.img(src=self._cw.uiprops['PUCE_DOWN'],
alt=xml_escape(self._cw._('action(s) on this selection')))
menu = PopupBoxMenu(label, isitem=False, link_class='actionsBox',
ident='%sActions' % divid)
--- a/web/views/xmlrss.py Wed May 05 18:10:07 2010 +0200
+++ b/web/views/xmlrss.py Wed May 05 18:10:33 2010 +0200
@@ -147,7 +147,7 @@
def call(self, **kwargs):
try:
- rss = self._cw.external_resource('RSS_LOGO')
+ rss = self._cw.uiprops['RSS_LOGO']
except KeyError:
self.error('missing RSS_LOGO external resource')
return
--- a/web/webconfig.py Wed May 05 18:10:07 2010 +0200
+++ b/web/webconfig.py Wed May 05 18:10:33 2010 +0200
@@ -23,8 +23,10 @@
import os
from os.path import join, exists, split
+from warnings import warn
from logilab.common.decorators import cached
+from logilab.common.deprecation import deprecated
from cubicweb.toolsutils import read_config
from cubicweb.cwconfig import CubicWebConfiguration, register_persistent_options, merge_options
@@ -208,7 +210,7 @@
))
def fckeditor_installed(self):
- return exists(self.ext_resources['FCKEDITOR_PATH'])
+ return exists(self.uiprops['FCKEDITOR_PATH'])
def eproperty_definitions(self):
for key, pdef in super(WebConfiguration, self).eproperty_definitions():
@@ -239,30 +241,6 @@
def vc_config(self):
return self.repository().get_versions()
- # mapping to external resources (id -> path) (`external_resources` file) ##
- ext_resources = {
- 'FAVICON': 'DATADIR/favicon.ico',
- 'LOGO': 'DATADIR/logo.png',
- 'RSS_LOGO': 'DATADIR/rss.png',
- 'HELP': 'DATADIR/help.png',
- 'CALENDAR_ICON': 'DATADIR/calendar.gif',
- 'SEARCH_GO':'DATADIR/go.png',
-
- 'FCKEDITOR_PATH': '/usr/share/fckeditor/',
-
- 'IE_STYLESHEETS': ['DATADIR/cubicweb.ie.css'],
- 'STYLESHEETS': ['DATADIR/cubicweb.css'],
- 'STYLESHEETS_PRINT': ['DATADIR/cubicweb.print.css'],
-
- 'JAVASCRIPTS': ['DATADIR/jquery.js',
- 'DATADIR/jquery.corner.js',
- 'DATADIR/jquery.json.js',
- 'DATADIR/cubicweb.compat.js',
- 'DATADIR/cubicweb.python.js',
- 'DATADIR/cubicweb.htmlhelpers.js'],
- }
-
-
def anonymous_user(self):
"""return a login and password to use for anonymous users. None
may be returned for both if anonymous connections are not allowed
@@ -276,26 +254,30 @@
user = unicode(user)
return user, passwd
- def has_resource(self, rid):
- """return true if an external resource is defined"""
- return bool(self.ext_resources.get(rid))
-
- @cached
def locate_resource(self, rid):
"""return the directory where the given resource may be found"""
return self._fs_locate(rid, 'data')
- @cached
def locate_doc_file(self, fname):
"""return the directory where the given resource may be found"""
return self._fs_locate(fname, 'wdoc')
- def _fs_locate(self, rid, rdirectory):
+ @cached
+ def _fs_path_locate(self, rid, rdirectory):
"""return the directory where the given resource may be found"""
path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())]
for directory in path:
if exists(join(directory, rdirectory, rid)):
- return join(directory, rdirectory)
+ return directory
+
+ def _fs_locate(self, rid, rdirectory):
+ """return the directory where the given resource may be found"""
+ directory = self._fs_path_locate(rid, rdirectory)
+ if directory is None:
+ return None
+ if rdirectory == 'data' and rid.endswith('.css'):
+ return self.uiprops.process_resource(join(directory, rdirectory), rid)
+ return join(directory, rdirectory)
def locate_all_files(self, rid, rdirectory='wdoc'):
"""return all files corresponding to the given resource"""
@@ -309,8 +291,8 @@
"""load instance's configuration files"""
super(WebConfiguration, self).load_configuration()
# load external resources definition
- self._build_ext_resources()
self._init_base_url()
+ self._build_ui_properties()
def _init_base_url(self):
# normalize base url(s)
@@ -320,29 +302,62 @@
if not self.repairing:
self.global_set_option('base-url', baseurl)
httpsurl = self['https-url']
- if httpsurl and httpsurl[-1] != '/':
- httpsurl += '/'
- if not self.repairing:
- self.global_set_option('https-url', httpsurl)
+ if httpsurl:
+ if httpsurl[-1] != '/':
+ httpsurl += '/'
+ if not self.repairing:
+ self.global_set_option('https-url', httpsurl)
+ if self.debugmode:
+ self.https_datadir_url = httpsurl + 'data/'
+ else:
+ self.https_datadir_url = httpsurl + 'data%s/' % self.instance_md5_version()
+ if self.debugmode:
+ self.datadir_url = baseurl + 'data/'
+ else:
+ self.datadir_url = baseurl + 'data%s/' % self.instance_md5_version()
- def _build_ext_resources(self):
- libresourcesfile = join(self.shared_dir(), 'data', 'external_resources')
- self.ext_resources.update(read_config(libresourcesfile))
+ def _build_ui_properties(self):
+ # self.datadir_url[:-1] to remove trailing /
+ from cubicweb.web.propertysheet import PropertySheet
+ self.uiprops = PropertySheet(
+ join(self.appdatahome, 'uicache'),
+ data=lambda x: self.datadir_url + x,
+ datadir_url=self.datadir_url[:-1])
+ self._init_uiprops(self.uiprops)
+ if self['https-url']:
+ self.https_uiprops = PropertySheet(
+ join(self.appdatahome, 'uicache'),
+ data=lambda x: self.https_datadir_url + x,
+ datadir_url=self.https_datadir_url[:-1])
+ self._init_uiprops(self.https_uiprops)
+
+ def _init_uiprops(self, uiprops):
+ libuiprops = join(self.shared_dir(), 'data', 'uiprops.py')
+ uiprops.load(libuiprops)
for path in reversed([self.apphome] + self.cubes_path()):
- resourcesfile = join(path, 'data', 'external_resources')
- if exists(resourcesfile):
- self.debug('loading %s', resourcesfile)
- self.ext_resources.update(read_config(resourcesfile))
- resourcesfile = join(self.apphome, 'external_resources')
+ self._load_ui_properties_file(uiprops, path)
+ self._load_ui_properties_file(uiprops, self.apphome)
+
+ def _load_ui_properties_file(self, uiprops, path):
+ resourcesfile = join(path, 'data', 'external_resources')
if exists(resourcesfile):
- self.debug('loading %s', resourcesfile)
- self.ext_resources.update(read_config(resourcesfile))
- for resource in ('STYLESHEETS', 'STYLESHEETS_PRINT',
- 'IE_STYLESHEETS', 'JAVASCRIPTS'):
- val = self.ext_resources[resource]
- if isinstance(val, str):
- files = [w.strip() for w in val.split(',') if w.strip()]
- self.ext_resources[resource] = files
+ warn('[3.9] %s file is deprecated, use an uiprops.py file'
+ % resourcesfile, DeprecationWarning)
+ datadir_url = uiprops.context['datadir_url']
+ for rid, val in read_config(resourcesfile).iteritems():
+ if rid in ('STYLESHEETS', 'STYLESHEETS_PRINT',
+ 'IE_STYLESHEETS', 'JAVASCRIPTS'):
+ val = [w.strip().replace('DATADIR', datadir_url)
+ for w in val.split(',') if w.strip()]
+ if rid == 'IE_STYLESHEETS':
+ rid = 'STYLESHEETS_IE'
+ else:
+ val = val.strip().replace('DATADIR', datadir_url)
+ uiprops[rid] = val
+ uipropsfile = join(path, 'uiprops.py')
+ if exists(uipropsfile):
+ self.debug('loading %s', uipropsfile)
+ uiprops.load(uipropsfile)
# static files handling ###################################################
@@ -369,3 +384,8 @@
def static_file_del(self, rpath):
if self.static_file_exists(rpath):
os.remove(join(self.static_directory, rpath))
+
+ @deprecated('[3.9] use _cw.uiprops.get(rid)')
+ def has_resource(self, rid):
+ """return true if an external resource is defined"""
+ return bool(self.uiprops.get(rid))
--- a/wsgi/handler.py Wed May 05 18:10:07 2010 +0200
+++ b/wsgi/handler.py Wed May 05 18:10:33 2010 +0200
@@ -100,9 +100,8 @@
NOTE: no pyro
"""
- def __init__(self, config, debug=None, vreg=None):
- self.appli = CubicWebPublisher(config, debug=debug, vreg=vreg)
- self.debugmode = debug
+ def __init__(self, config, vreg=None):
+ self.appli = CubicWebPublisher(config, vreg=vreg)
self.config = config
self.base_url = None
# self.base_url = config['base-url'] or config.default_base_url()