Example #1
0
def setup(configuration_filename=None, use_tori_custom_error_page=False, support_unicode=True, enable_compression=False, auto_config=False, additional_config=None):
    """
    Set up the environment
    
    `configuration_filename` is the name of the configuration file in XML. By
    default, it is null and instruct the system to use the default settings.
    
    `use_tori_custome_error_page` is a flag to tell whether the application
    process should use the nicer custom error page. Please note that enabling
    this feature will slightly decrease performance. This is a known issue
    for CherryPy 3.1+.
    
    `support_unicode` is a flag to tell whether the application process should
    support unicode. Please note that enabling this feature will slightly decrease performance.
    
    `enable_compression` is a flag to tell whether the application process
    should compress the output. Please note that enabling this feature will
    decrease performance and possibly mess up JavaScript. This feature is
    disabled by default when the X/HTML document contains **pre** elements.
    Use with caution.
    
    `auto_config` is a flag to tell whether the application process
    should create a configuration file (named by `configuration_filename`)
    automatically if not existed. When the function detect that the configuration
    file doesn't exist, with this flag enabled, it will create the configuration
    file with the default configuration. Then, it will terminated the process.
    This gives the developers an opportunity to config the settings before proceeding.
    
    `additional_config` is a configuration dictionary for CherryPy 3.1+. It is
    for adding some additional configuration directly to CherryPy which is the
    underlying framework. Please note that this will override any configuration
    from the configuration file.
    """
    
    global mode
    global base_uri
    global base_path
    global path
    global error_template
    global static_routing
    global settings
    global template
    global debug
    
    # Initialization
    __base_config_key = 'tools.static'
    mode = 'local'
    port = 8080
    base_uri = ''
    base_path = ''
    path = {}
    error_template = None
    static_routing = {}
    default_config = {
        'global': {
            'server.thread_pool':       10,
            'server.socket_queue_size': 10,
            'server.socket_host':       '0.0.0.0',
            'server.socket_port':       port,
            'tools.decode.encoding':    'utf-8',
            'tools.encode.encoding':    'utf-8',
            'tools.decode.on':          support_unicode,
            'tools.encode.on':          support_unicode,
            'tools.gzip.on':            enable_compression,
            'log.screen':               False                          # Disable trackback information
        }
    }
    
    if use_tori_custom_error_page:
        # Use the custom error response from Tori
        default_config['global']['error_page.default'] = DefaultErrorPage.response
    
    settings = {}
    standard_config = """<?xml version="1.0" encoding="UTF-8"?>
    <webconfig>
        <mode>local</mode>
        <rendering>mako</rendering>
        <base_path>
            <static>static</static>
            <template>template</template>
            <session>memory</session>
        </base_path>
        <!--
        <port>8080</port>
        -->
        <base_uri>/</base_uri>
        <static_routing>
        <!--
            <file link="favicon.ico" ref="favicon.ico" />
            <dir link="image" ref="image" />
            <dir link="css" ref="css" />
            <dir link="js" ref="js" />
        -->
        </static_routing>
        <settings>
            <option name="debug">false</option>
            <option name="text_minification">false</option>
            <option name="no_cache">true</option>
        </settings>
    </webconfig>
    """
    
    # Get the reference to the calling function
    f = sys._getframe(1)
    c = f.f_code
    reference_to_caller = c.co_filename
    
    # Base path
    base_path = os.path.abspath(os.path.dirname(os.path.abspath(reference_to_caller)))
    
    # Get the location of the given the configuration files
    target_destination = configuration_filename
    if not re.search('^/', configuration_filename):
        target_destination = "%s/%s" % (base_path, configuration_filename)
    
    try:
        # Load the configuration files
        xmldoc = Kotoba()
        xmldoc.read(target_destination)
    except:
        if auto_config:
            fs.write(target_destination, standard_config, "w")
            xmldoc = Kotoba(target_destination)
        else:
            raise WebFrameworkException("Error while reading configuration from %s" % target_destination)
    
    try:
        # Get operational mode
        xml_on_mode = xmldoc.get("mode")
        if len(xml_on_mode) > 0:
            mode = xml_on_mode.data()
    except:
        raise WebFrameworkException("Error while determining the running mode")
    
    try:
        # Get operational port. This will be ignored in GAE mode
        xml_on_port = xmldoc.get("port")
        if len(xml_on_port) > 0:
            port = base.convertToInteger(xml_on_port.data())
            default_config['global']['server.socket_port'] = port
    except:
        raise WebFrameworkException("Error while determining the running port")
    
    try:
        # Store the basic paths in the memory. Make the directory for the destination if necessary.
        path_indices = xmldoc.get('base_path *')
        for path_index in path_indices:
            pathName = path_index.name
            path[pathName] = os.path.join(base_path, path_index.data())
            make_directory_if_not_existed(path[pathName])
    except:
        raise WebFrameworkException("Error while setting up the directories")
    
    try:
        # Get application settings
        xml_on_settings = xmldoc.get("settings option")
        if xml_on_settings:
            for option in xml_on_settings:
                option_data = option.data()
                
                option_data_as_floating_number = base.convertToFloatingNumber(option_data)
                option_data_as_integer = base.convertToInteger(option_data)
                option_data_as_boolean = base.convertToBoolean(option_data)
                
                if re.match("^\d+(\.\d+)?$", option_data):
                    if option_data_as_floating_number is not None:
                        # the option data is integer-convertible.
                        option_data = option_data_as_floating_number
                    elif option_data_as_integer is not None:
                        # the option data is integer-convertible.
                        option_data = option_data_as_integer
                elif option_data_as_boolean is not None:
                    # the option data is boolean-convertible.
                    option_data = option_data_as_boolean
                
                settings[option.attrs['name']] = option_data
        
        # Recognized options by framework (with the default value)
        recognized_options = {
            'debug': True,
            'no_cache': True,
            'direct_rendering': True,
            'text_minification': False
        }
        
        for recognized_option, default_option in recognized_options.iteritems():
            if recognized_option not in settings:
                settings[recognized_option] = default_option
        
        if 'debug' in settings:
            debug = settings['debug']
    except:
        raise WebFrameworkException("Error while reading anonymous settings for this application")
    
    try:
        # Get the base URI
        base_uri = xmldoc.get('base_uri').data()
        base_uri = base_uri.strip()
        base_uri = re.sub("^/", "", base_uri)
        base_uri = re.sub("/$", "", base_uri)
        
        static_routing[base_uri + '/'] = {
            'tools.caching.on':             True,
            'tools.caching.cache_class':    cherrypy.lib.caching.MemoryCache,
            'tools.caching.delay':          259200, # 3 days
            'tools.caching.maxobjsize':     26214400, # 25 MB
            'tools.caching.maxsize':        104857600, # 100 MB
            'tools.sessions.on':            True,
            'tools.sessions.timeout':       10,
            'tools.sessions.storage_type':  'file',
            'tools.sessions.storage_path':  path['session']
        }
        
        default_config['global']['tools.staticdir.root'] = path['static']
        default_config['global']['tools.staticfile.root'] = path['static']
        
        if settings['no_cache']:
            doc_root_settings = static_routing[base_uri + '/']
            doc_root_settings['tools.caching.on'] = False
        
        if mode == ServiceMode.GAE:
            doc_root_settings = static_routing[base_uri + '/']
            doc_root_settings['tools.caching.on'] = False
            del doc_root_settings['tools.sessions.storage_type']
            del doc_root_settings['tools.sessions.storage_path']
        
        xmldocOnstatic_routing = xmldoc.get('static_routing file, static_routing dir')
        
        for referenceSetting in xmldocOnstatic_routing:
            __type = referenceSetting.name
            __ref = referenceSetting.attrs['ref']
            __link = referenceSetting.attrs['link']
            __cBaseKey = "%s%s" % (__base_config_key, __type)
            __cKeyFlag = "%s.on" % (__cBaseKey)
            __cKeyPath = "%s.%s" % (__cBaseKey, __type)
            
            if __type == 'file' and __ref is not None:
                __cKeyPath += 'name'
                #__ref = os.path.join(path['static'], __ref)
            
            if __type == 'dir':
                make_directory_if_not_existed(os.path.join(path['static'], __ref))
            
            static_routing[base_uri + '/' + __link] = {
                str(__cKeyFlag): True,
                str(__cKeyPath): __ref
            }
        
        items_in_static = fs.browse(path['static'])
        for item_type in items_in_static:
            for item in items_in_static[item_type]:
                if item[0] == ".": continue
                __type = item_type == "files" and "file" or "dir"
                __ref = item
                __link = item
                
                __route_address = '%s/%s' % (base_uri, __link)
                if __route_address in static_routing:
                    continue
                
                __cBaseKey = "%s%s" % (__base_config_key, __type)
                __cKeyFlag = "%s.on" % (__cBaseKey)
                __cKeyPath = "%s.%s" % (__cBaseKey, __type)
                
                if __type == 'file' and __ref is not None:
                    __cKeyPath += 'name'
                
                if __type == 'dir':
                    make_directory_if_not_existed(os.path.join(path['static'], __ref))
                
                static_routing[__route_address] = {
                    str(__cKeyFlag): True,
                    str(__cKeyPath): __ref
                }
    
    except:
        raise WebFrameworkException("Error while setting up routing")
    
    try:
        # Determine the template system
        xml_on_rendering = xmldoc.get("rendering")
        if xml_on_rendering:
            template['use'] = xml_on_rendering.data()
        else:
            template['use'] = 'mako'
        
        if template['use'] == 'tenjin':
            enable_caching = not settings['no_cache']
            if enable_caching:
                enable_caching = MemoryCacheStorage()
            template['tenjin']['engine'] = Engine(
                path=[path['template']],
                cache = enable_caching
            )
        else:
            template_filesystem_checks = settings['no_cache']
            if mode == ServiceMode.GAE:
                template['mako']['engine'] = TemplateLookup(
                    directories = [path['template']],
                    filesystem_checks=template_filesystem_checks,
                    **template['mako']['options']
                )
            else:
                template['mako']['engine'] = TemplateLookup(
                    directories = [path['template']],
                    module_directory=os.path.join(path['session'], 'cache', 'template'), # disable for GAE apps
                    filesystem_checks=template_filesystem_checks,
                    **template['mako']['options']
                )
            template['mako']['get'] = template['mako']['engine'].get_template
    except:
        raise WebFrameworkException("Error while setting up the template system")
    
    try:
        # Add custom error pages
        xml_on_error_template = xmldoc.get('error_template')
        if xml_on_error_template:
            error_template = xml_on_error_template.data()
            error_template = fs.read(os.path.join(path['template'], error_template))
    except:
        raise WebFrameworkException("Error while setting up a custom error error page.")
    
    
    cherrypy.config.update(default_config)
    
    if additional_config is not None:
        cherrypy.config.update(additional_config)
Example #2
0
    def default(self, *paths):
        cacheBlockName = "%s/%s" % (self.name, '/'.join(paths))
        if self.isGET() and self.cache(cacheBlockName) is not None:
            return self.cache(cacheBlockName)

        # Response context
        response_context = None

        # Disable editor mode by default
        editor_called = False

        # Page mode: reading, editing, overview (TOC)
        page_mode = len(paths) > 1 and paths[-1] or None
        if page_mode not in [RegisteredMode.edit_content, RegisteredMode.list_page_hierarchy]:
            page_mode = None # Assume the reading mode

        # Get the whole request path (in array bit) without the page mode
        path = len(paths) == 0 and RegisteredPage.index or '/'.join(page_mode == None and paths or paths[:-1])

        # Breadcrumb
        breadcrumb = []
        requested_path = None
        if len(paths) > 0:
            urlpath = []
            requested_path = '/'.join(paths[1:])
            for p in paths:
                urlpath.append(p)
                current_path = '/'.join(urlpath)
                p = self.convert_to_full_name(p)
                breadcrumb.append({
                    'URL': current_path,
                    'Full name': p,
                    'Short name': self.convert_to_shortname(p)
                })
            del urlpath

        current_page = len(breadcrumb) > 0 and breadcrumb[-1] or None

        page_object = self.get_page_object(path)
        backup_page_object = self.get_backup_page_object(path)

        if self.isGET() and page_mode == None:
            is_index_page = path == RegisteredPage.index
            do_page_existed = is_index_page or page_object is not None or backup_page_object is not None

            # General information
            general_information = {
                'breadcrumb':  breadcrumb,
                'current_page': current_page
            }

            # [General procedure]
            # 1. Retrieve the subpages (1 level deep) if necessary
            # 2. Render the page object by prioritizing the (HTML) sources in the following order:
            #   1. Database entry
            #   2. Flat file from archive
            #   3. Flat file from template

            # Prioritize the one in the database first.
            if page_object is not None and not self.use_static_file:
                pass
            # Prioritize the one in the database first.
            if page_object is not None and self.use_static_file:
                # Initialize data
                templateID = "default" # templateID
                subpages = None # list of subpages
                path_to_page_object = self.get_absolute_path_to_page_object(path) # path to the page object
                pre_processed_page_data = fs.read(path_to_page_object) # pre-processed page data

                # Look for requirements
                local_flags = re.search("<!--\s*require\s*([a-zA-Z0-9\-\+_]+)\s*-->", pre_processed_page_data)
                if local_flags is not None:
                    # If detect the traversal flag for subpages, do it.
                    if 'subpages' in local_flags.groups():
                        subpages = []
                        possible_directory = self.remove_extension(path_to_page_object)

                        if fs.exists(possible_directory):
                            raw_subpages = fs.browse(possible_directory, True)['files']
                            for p in raw_subpages:
                                p = re.sub("^" + possible_directory + "/", "", p)
                                p = self.remove_extension(p)
                                if p == "index" or p[0] == "_":
                                    continue
                                subpages.append((self.convert_to_full_name(p), p))
                        general_information['subpages'] = subpages

                    # If detect the traversal flag for neighbours, do it.
                    if 'neighbours' in local_flags.groups():
                        neighbours = []
                        possible_directory = re.sub("/[^/]+$", "", self.remove_extension(path_to_page_object))

                        if fs.exists(possible_directory):
                            raw_subpages = fs.browse(possible_directory, True)['files']
                            for p in raw_subpages:
                                p = re.sub("^" + possible_directory + "/", "", p)
                                p = self.remove_extension(p)
                                if p == "index" or p[0] == "_":
                                    continue
                                neighbours.append((self.convert_to_full_name(p), p))
                        general_information['neighbours'] = neighbours

                    # If require syntax highlighting, add the JS code
                    if 'syntax_highlight' in local_flags.groups():
                        pre_processed_page_data += self.__JS_code_rendering

                # Determine if it needs a special template
                local_flags = re.search("<!--\s*template\s*([^ ]+)\s*-->", pre_processed_page_data)
                if local_flags is not None:
                    local_templateID = local_flags.groups()[0]
                    if local_templateID in self.__templates:
                        templateID = local_templateID

                response_context = self.local_render(pre_processed_page_data, **general_information)
                response_context = self.local_render(self.__templates[templateID], response_context = response_context, **general_information)
            elif backup_page_object is not None:
                response_context = self.local_render(backup_page_object, **general_information)

            if not do_page_existed and self.isAdmin() and False:
                editor_called = True

        elif self.isGET() and page_mode in RegisteredMode.edit_content:
            editor_called = True

        elif self.isGET() and page_mode == RegisteredMode.list_page_hierarchy:
            response_context = tori.UserInterface.response("Table of content (index mode) requested")

        elif self.isPOST():
            response_context = tori.UserInterface.response("Creating/updating called")

        elif self.isDELETE():
            response_context = tori.UserInterface.response("Creating/updating called")

        # endif

        if editor_called:
            response_context = self.load_editor(page_mode, '/'.join(len(paths) > 1 and paths[1:-1] or []))
        elif not editor_called and response_context is None:
            self.respond_status(404)
        else:
            self.cache(cacheBlockName, response_context, self.__cache_duration)

        return response_context