Merge github.com:web2py/web2py

This commit is contained in:
Michele Comitini
2012-10-10 21:21:04 +02:00
18 changed files with 375 additions and 197 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.1.0 (2012-10-08 22:09:18) dev Version 2.1.0 (2012-10-10 12:20:21) dev
+1 -1
View File
@@ -39,7 +39,7 @@ def deploy():
form = SQLFORM.factory( form = SQLFORM.factory(
Field('appcfg',default=GAE_APPCFG,label=T('Path to appcfg.py'), Field('appcfg',default=GAE_APPCFG,label=T('Path to appcfg.py'),
requires=EXISTS(error_message=T('file not found'))), requires=EXISTS(error_message=T('file not found'))),
Field('google_application_id',requires=IS_ALPHANUMERIC(),label=T('Google Application Id')), Field('google_application_id',requires=IS_MATCH('[\w\-]+'),label=T('Google Application Id')),
Field('applications','list:string', Field('applications','list:string',
requires=IS_IN_SET(apps,multiple=True), requires=IS_IN_SET(apps,multiple=True),
label=T('web2py apps to deploy')), label=T('web2py apps to deploy')),
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+82 -112
View File
@@ -11,7 +11,7 @@ Basic caching classes and methods
- Cache - The generic caching object interfacing with the others - Cache - The generic caching object interfacing with the others
- CacheInRam - providing caching in ram - CacheInRam - providing caching in ram
- CacheInDisk - provides caches on disk - CacheOnDisk - provides caches on disk
Memcache is also available via a different module (see gluon.contrib.memcache) Memcache is also available via a different module (see gluon.contrib.memcache)
@@ -130,22 +130,27 @@ class CacheInRam(CacheAbstract):
meta_storage = {} meta_storage = {}
def __init__(self, request=None): def __init__(self, request=None):
self.locker.acquire() self.initialized = False
self.request = request self.request = request
def initialize(self):
if self.initialized: return
else: self.initialized = True
self.locker.acquire()
request = self.request
if request: if request:
app = request.application app = request.application
else: else:
app = '' app = ''
if not app in self.meta_storage: if not app in self.meta_storage:
self.storage = self.meta_storage[app] = {CacheAbstract.cache_stats_name: { self.storage = self.meta_storage[app] = {
'hit_total': 0, CacheAbstract.cache_stats_name: {'hit_total': 0, 'misses': 0}}
'misses': 0,
}}
else: else:
self.storage = self.meta_storage[app] self.storage = self.meta_storage[app]
self.locker.release() self.locker.release()
def clear(self, regex=None): def clear(self, regex=None):
self.initialize()
self.locker.acquire() self.locker.acquire()
storage = self.storage storage = self.storage
if regex is None: if regex is None:
@@ -154,10 +159,7 @@ class CacheInRam(CacheAbstract):
self._clear(storage, regex) self._clear(storage, regex)
if not CacheAbstract.cache_stats_name in storage.keys(): if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = { storage[CacheAbstract.cache_stats_name] = {'hit_total': 0,'misses': 0}
'hit_total': 0,
'misses': 0,
}
self.locker.release() self.locker.release()
@@ -172,6 +174,7 @@ class CacheInRam(CacheAbstract):
3) would work unless we deepcopy no storage and retrival which would make things slow. 3) would work unless we deepcopy no storage and retrival which would make things slow.
Anyway. You can deepcopy explicitly in the function generating the value to be cached. Anyway. You can deepcopy explicitly in the function generating the value to be cached.
""" """
self.initialize()
dt = time_expire dt = time_expire
now = time.time() now = time.time()
@@ -200,6 +203,7 @@ class CacheInRam(CacheAbstract):
return value return value
def increment(self, key, value=1): def increment(self, key, value=1):
self.initialize()
self.locker.acquire() self.locker.acquire()
try: try:
if key in self.storage: if key in self.storage:
@@ -226,58 +230,65 @@ class CacheOnDisk(CacheAbstract):
Values stored in disk cache must be pickable. Values stored in disk cache must be pickable.
""" """
speedup_checks = set() def _close_shelve_and_unlock(self):
try:
if self.storage:
self.storage.close()
finally:
if self.locker and self.locked:
portalocker.unlock(self.locker)
self.locker.close()
self.locked = False
def _open_shelf_with_lock(self): def _open_shelve_and_lock(self):
"""Open and return a shelf object, obtaining an exclusive lock """Open and return a shelf object, obtaining an exclusive lock
on self.locker first. Replaces the close method of the on self.locker first. Replaces the close method of the
returned shelf instance with one that releases the lock upon returned shelf instance with one that releases the lock upon
closing.""" closing."""
def _close(self):
try: storage = None
shelve.Shelf.close(self) locker = None
finally: locked = False
portalocker.unlock(self.locker)
self.locker.close()
storage, locker, locker_locked = None, None, False
try: try:
locker = open(self.locker_name, 'a') locker = locker = open(self.locker_name, 'a')
portalocker.lock(locker, portalocker.LOCK_EX) portalocker.lock(locker, portalocker.LOCK_EX)
locker_locked = True locked = True
storage = shelve.open(self.shelve_name) try:
storage.close = _close.__get__(storage, shelve.Shelf) storage = shelve.open(self.shelve_name)
storage.locker = locker except:
except Exception: logger.error('corrupted cache file %s, will try rebuild it' \
logger.error('corrupted cache file %s, will try to delete and recreate it!' % (self.shelve_name)) % (self.shelve_name))
storage = None
if not storage and os.path.exists(self.shelve_name):
os.unlink(self.shelve_name)
storage = shelve.open(self.shelve_name)
if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = {'hit_total':0, 'misses': 0}
storage.sync()
except Exception, e:
if storage: if storage:
storage.close() storage.close()
storage = None storage = None
if locked:
try: portalocker.unlock(locker)
os.unlink(self.shelve_name) locker.close()
storage = shelve.open(self.shelve_name) locked = False
storage.close = _close.__get__(storage, shelve.Shelf) raise RuntimeError, 'unable to create/re-create cache file %s' % self.shelve_name
storage.locker = locker self.locker = locker
if not CacheAbstract.cache_stats_name in storage.keys(): self.locked = locked
storage[CacheAbstract.cache_stats_name] = { self.storage = storage
'hit_total': 0,
'misses': 0,
}
storage.sync()
except (IOError, OSError):
logger.warn('unable to delete and recreate cache file %s' % self.shelve_name)
if storage:
storage.close()
storage = None
if locker_locked:
portalocker.unlock(locker)
if locker:
locker.close()
return storage return storage
def __init__(self, request, folder=None): def __init__(self, request=None, folder=None):
self.initialized = False
self.request = request self.request = request
self.folder = folder
def initialize(self):
if self.initialized: return
else: self.initialized = True
folder = self.folder
request = self.request
# Lets test if the cache folder exists, if not # Lets test if the cache folder exists, if not
# we are going to create it # we are going to create it
@@ -291,93 +302,54 @@ class CacheOnDisk(CacheAbstract):
self.locker_name = os.path.join(folder,'cache.lock') self.locker_name = os.path.join(folder,'cache.lock')
self.shelve_name = os.path.join(folder,'cache.shelve') self.shelve_name = os.path.join(folder,'cache.shelve')
speedup_key = (folder,CacheAbstract.cache_stats_name)
if not speedup_key in self.speedup_checks or \
not os.path.exists(self.shelve_name):
try:
storage = self._open_shelf_with_lock()
try:
if not CacheAbstract.cache_stats_name in storage:
storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0,
'misses': 0,
}
storage.sync()
finally:
storage.close()
self.speedup_checks.add(speedup_key)
except ImportError:
pass # no module _bsddb, ignoring exception now so it makes a ticket only if used
def clear(self, regex=None): def clear(self, regex=None):
storage = self._open_shelf_with_lock() self.initialize()
storage = self._open_shelve_and_lock()
try: try:
if regex is None: if regex is None:
storage.clear() storage.clear()
else: else:
self._clear(storage, regex) self._clear(storage, regex)
if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0,
'misses': 0,
}
storage.sync() storage.sync()
finally: finally:
storage.close() self._close_shelve_and_unlock()
def __call__(self, key, f, def __call__(self, key, f,
time_expire = DEFAULT_TIME_EXPIRE): time_expire = DEFAULT_TIME_EXPIRE):
self.initialize()
dt = time_expire dt = time_expire
storage = self._open_shelve_and_lock()
storage = self._open_shelf_with_lock()
try: try:
item = storage.get(key, None) item = storage.get(key, None)
storage[CacheAbstract.cache_stats_name]['hit_total'] += 1
if item and f is None: if item and f is None:
del storage[key] del storage[key]
storage.sync()
storage[CacheAbstract.cache_stats_name] = { now = time.time()
'hit_total': storage[CacheAbstract.cache_stats_name]['hit_total'] + 1, if f is None:
'misses': storage[CacheAbstract.cache_stats_name]['misses'] value = None
} elif item and (dt is None or item[0] > now - dt):
value = item[1]
storage.sync() else:
value = f()
storage[key] = (now, value)
storage[CacheAbstract.cache_stats_name]['misses']+=1
storage.sync()
finally: finally:
if storage: self._close_shelve_and_unlock()
storage.close()
now = time.time()
if f is None:
return None
if item and (dt is None or item[0] > now - dt):
return item[1]
value = f()
storage = self._open_shelf_with_lock()
try:
storage[key] = (now, value)
storage[CacheAbstract.cache_stats_name] = {
'hit_total': storage[CacheAbstract.cache_stats_name]['hit_total'],
'misses': storage[CacheAbstract.cache_stats_name]['misses'] + 1
}
storage.sync()
finally:
if storage:
storage.close()
return value return value
def increment(self, key, value=1): def increment(self, key, value=1):
storage = self._open_shelf_with_lock() self.initialize()
storage = self._open_shelve_and_lock()
try: try:
if key in storage: if key in storage:
value = storage[key][1] + value value = storage[key][1] + value
storage[key] = (time.time(), value) storage[key] = (time.time(), value)
storage.sync() storage.sync()
finally: finally:
if storage: self._close_shelve_and_unlock()
storage.close()
return value return value
class CacheAction(object): class CacheAction(object):
@@ -423,10 +395,9 @@ class Cache(object):
the global request object the global request object
""" """
# GAE will have a special caching # GAE will have a special caching
if have_settings and settings.global_settings.web2py_runtime_gae: if have_settings and settings.global_settings.web2py_runtime_gae:
from contrib.gae_memcache import MemcacheClient from contrib.gae_memcache import MemcacheClient
self.ram=self.disk=MemcacheClient(request) self.ram = self.disk = MemcacheClient(request)
else: else:
# Otherwise use ram (and try also disk) # Otherwise use ram (and try also disk)
self.ram = CacheInRam(request) self.ram = CacheInRam(request)
@@ -489,7 +460,6 @@ class Cache(object):
cache_model(prefix + key, f, time_expire) cache_model(prefix + key, f, time_expire)
def lazy_cache(key=None,time_expire=None,cache_model='ram'): def lazy_cache(key=None,time_expire=None,cache_model='ram'):
""" """
can be used to cache any function including in modules, can be used to cache any function including in modules,
+1
View File
@@ -387,6 +387,7 @@ def build_environment(request, response, session, store_current=True):
t = environment['T'] = translator(request) t = environment['T'] = translator(request)
c = environment['cache'] = Cache(request) c = environment['cache'] = Cache(request)
if store_current: if store_current:
current.globalenv = environment current.globalenv = environment
current.request = request current.request = request
+1 -1
View File
@@ -44,7 +44,7 @@ class MemcacheClient(Client):
obj = self.get(key) obj = self.get(key)
if obj: if obj:
value = obj[1] + value value = obj[1] + value
self.set((time.time(), value)) self.set(key, (time.time(), value))
return value return value
def clear(self, key = None): def clear(self, key = None):
+198
View File
@@ -0,0 +1,198 @@
"""
Developed by niphlod@gmail.com
"""
import redis
from redis.exceptions import ConnectionError
from gluon import current
from gluon.storage import Storage
import cPickle as pickle
import time
import re
import logging
import thread
logger = logging.getLogger("web2py.session.redis")
locker = thread.allocate_lock()
def RedisSession(*args, **vars):
"""
Usage example: put in models
sessiondb = RedisSession('localhost:6379',db=0, debug=True)
session.connect(request, response, db = sessiondb)
Simple slip-in storage for session
"""
locker.acquire()
try:
if not hasattr(RedisSession, 'redis_instance'):
RedisSession.redis_instance = RedisClient(*args, **vars)
finally:
locker.release()
return RedisSession.redis_instance
class RedisClient(object):
meta_storage = {}
MAX_RETRIES = 5
RETRIES = 0
def __init__(self, server='localhost:6379', db=None, debug=False, session_expiry=False):
"""session_expiry can be an integer, in seconds, to set the default expiration
of sessions. The corresponding record will be deleted from the redis instance,
and there's virtually no need to run sessions2trash.py
"""
self.server = server
self.db = db or 0
host,port = (self.server.split(':')+['6379'])[:2]
port = int(port)
self.debug = debug
if current and current.request:
self.app = current.request.application
else:
self.app = ''
self.r_server = redis.Redis(host=host, port=port, db=self.db)
self.tablename = None
self.session_expiry = session_expiry
def get(self, what, default):
return self.tablename
def Field(self, fieldname, type='string', length=None, default=None,
required=False,requires=None):
return None
def define_table(self,tablename,*fields,**args):
if not self.tablename:
self.tablename = MockTable(self, self.r_server, tablename, self.session_expiry)
return self.tablename
def __getitem__(self, key):
return self.tablename
def __call__(self, where=''):
q = self.tablename.query
return q
def commit(self):
#this is only called by session2trash.py
pass
class MockTable(object):
def __init__(self, db, r_server, tablename, session_expiry):
self.db = db
self.r_server = r_server
self.tablename = tablename
#set the namespace for sessions of this app
self.keyprefix = 'w2p:sess:%s' % tablename.replace('web2py_session_', '')
#fast auto-increment id (needed for session handling)
self.serial = "%s:serial" % self.keyprefix
#index of all the session keys of this app
self.id_idx = "%s:id_idx" % self.keyprefix
#remember the session_expiry setting
self.session_expiry = session_expiry
def getserial(self):
#return an auto-increment id
return "%s" % self.r_server.incr(self.serial, 1)
def __getattr__(self, key):
if key == 'id':
#return a fake query. We need to query it just by id for normal operations
self.query = MockQuery(field='id', db=self.r_server, prefix=self.keyprefix, session_expiry=self.session_expiry)
return self.query
elif key == '_db':
#needed because of the calls in sessions2trash.py and globals.py
return self.db
def insert(self, **kwargs):
#usually kwargs would be a Storage with several keys:
#'locked', 'client_ip','created_datetime','modified_datetime'
#'unique_key', 'session_data'
#retrieve a new key
newid = self.getserial()
key = "%s:%s" % (self.keyprefix, newid)
#add it to the index
self.r_server.sadd(self.id_idx, key)
#set a hash key with the Storage
self.r_server.hmset(key, kwargs)
if self.session_expiry:
self.r_server.expire(key, self.session_expiry)
return newid
class MockQuery(object):
"""a fake Query object that supports querying by id
and listing all keys. No other operation is supported
"""
def __init__(self, field=None, db=None, prefix=None, session_expiry=False):
self.field = field
self.value = None
self.db = db
self.keyprefix = prefix
self.op = None
self.session_expiry = session_expiry
def __eq__(self, value, op='eq'):
self.value = value
self.op = op
def __gt__(self, value, op='ge'):
self.value = value
self.op = op
def select(self):
if self.op == 'eq' and self.field == 'id' and self.value:
#means that someone wants to retrieve the key self.value
rtn = self.db.hgetall("%s:%s" % (self.keyprefix, self.value))
if rtn == dict():
#return an empty resultset for non existing key
return []
else:
return [Storage(rtn)]
elif self.op == 'ge' and self.field == 'id' and self.value == 0:
#means that someone wants the complete list
rtn = []
id_idx = "%s:id_idx" % self.keyprefix
#find all session keys of this app
allkeys = self.db.smembers(id_idx)
for sess in allkeys:
val = self.db.hgetall(sess)
if val == dict():
if self.session_expiry:
#clean up the idx, because the key expired
self.db.srem(id_idx, sess)
continue
else:
continue
val = Storage(val)
#add a delete_record method (necessary for sessions2trash.py)
val.delete_record = RecordDeleter(self.db, sess, self.keyprefix)
rtn.append(val)
return rtn
else:
raise Exception("Operation not supported")
def update(self, **kwargs):
#means that the session has been found and needs an update
if self.op == 'eq' and self.field == 'id' and self.value:
return self.db.hmset("%s:%s" % (self.keyprefix, self.value), kwargs)
class RecordDeleter(object):
"""Dumb record deleter to support sessions2trash.py"""
def __init__(self, db, key, keyprefix):
self.db, self.key, self.keyprefix = db, key, keyprefix
def __call__(self):
id_idx = "%s:id_idx" % self.keyprefix
#remove from the index
self.db.srem(id_idx, self.key)
#remove the key itself
self.db.delete(self.key)
+3 -3
View File
@@ -3981,8 +3981,8 @@ class DatabaseStoredFile:
self.data += data self.data += data
def close_connection(self): def close_connection(self):
self.db.executesql("DELETE FROM web2py_filesystem WHERE path='%s'" \ self.db.executesql(
% self.filename) "DELETE FROM web2py_filesystem WHERE path='%s'" % self.filename)
query = "INSERT INTO web2py_filesystem(path,content) VALUES ('%s','%s')"\ query = "INSERT INTO web2py_filesystem(path,content) VALUES ('%s','%s')"\
% (self.filename, self.data.replace("'","''")) % (self.filename, self.data.replace("'","''"))
self.db.executesql(query) self.db.executesql(query)
@@ -4007,7 +4007,7 @@ class UseDatabaseStoredFile:
return DatabaseStoredFile(self.db,filename,mode) return DatabaseStoredFile(self.db,filename,mode)
def file_close(self, fileobj): def file_close(self, fileobj):
fileobj.close() fileobj.close_connection()
def file_delete(self,filename): def file_delete(self,filename):
query = "DELETE FROM web2py_filesystem WHERE path='%s'" % filename query = "DELETE FROM web2py_filesystem WHERE path='%s'" % filename
+10 -8
View File
@@ -417,7 +417,7 @@ class Response(Storage):
return handler(request, self, methods) return handler(request, self, methods)
def toolbar(self): def toolbar(self):
from html import DIV, SCRIPT, BEAUTIFY, TAG, URL from html import DIV, SCRIPT, BEAUTIFY, TAG, URL, A
BUTTON = TAG.button BUTTON = TAG.button
admin = URL("admin","default","design", admin = URL("admin","default","design",
args=current.request.application) args=current.request.application)
@@ -438,19 +438,21 @@ class Response(Storage):
dbstats = [] # if no db or on GAE dbstats = [] # if no db or on GAE
dbtables = {} dbtables = {}
u = web2py_uuid() u = web2py_uuid()
backtotop = A('Back to top', _href="#totop-%s" % u)
return DIV( return DIV(
BUTTON('design',_onclick="document.location='%s'" % admin), BUTTON('design',_onclick="document.location='%s'" % admin),
BUTTON('request',_onclick="jQuery('#request-%s').slideToggle()"%u), BUTTON('request',_onclick="jQuery('#request-%s').slideToggle()"%u),
DIV(BEAUTIFY(current.request),_class="hidden",_id="request-%s"%u),
BUTTON('session',_onclick="jQuery('#session-%s').slideToggle()"%u),
DIV(BEAUTIFY(current.session),_class="hidden",_id="session-%s"%u),
BUTTON('response',_onclick="jQuery('#response-%s').slideToggle()"%u), BUTTON('response',_onclick="jQuery('#response-%s').slideToggle()"%u),
DIV(BEAUTIFY(current.response),_class="hidden",_id="response-%s"%u), BUTTON('session',_onclick="jQuery('#session-%s').slideToggle()"%u),
BUTTON('db tables',_onclick="jQuery('#db-tables-%s').slideToggle()"%u), BUTTON('db tables',_onclick="jQuery('#db-tables-%s').slideToggle()"%u),
DIV(BEAUTIFY(dbtables),_class="hidden",_id="db-tables-%s"%u),
BUTTON('db stats',_onclick="jQuery('#db-stats-%s').slideToggle()"%u), BUTTON('db stats',_onclick="jQuery('#db-stats-%s').slideToggle()"%u),
DIV(BEAUTIFY(dbstats),_class="hidden",_id="db-stats-%s"%u), DIV(BEAUTIFY(current.request), backtotop,_class="hidden",_id="request-%s"%u),
DIV(BEAUTIFY(current.session), backtotop, _class="hidden",_id="session-%s"%u),
DIV(BEAUTIFY(current.response), backtotop, _class="hidden",_id="response-%s"%u),
DIV(BEAUTIFY(dbtables), backtotop, _class="hidden",_id="db-tables-%s"%u),
DIV(BEAUTIFY(dbstats), backtotop, _class="hidden",_id="db-stats-%s"%u),
SCRIPT("jQuery('.hidden').hide()") SCRIPT("jQuery('.hidden').hide()")
,_id="totop-%s" % u
) )
class Session(Storage): class Session(Storage):
@@ -482,7 +484,7 @@ class Session(Storage):
if not masterapp: if not masterapp:
masterapp = request.application masterapp = request.application
response.session_id_name = 'session_id_%s' % masterapp.lower() response.session_id_name = 'session_id_%s' % masterapp.lower()
# Load session data from cookie # Load session data from cookie
cookies = request.cookies cookies = request.cookies
+26 -22
View File
@@ -88,7 +88,7 @@ from contenttype import contenttype
from dal import BaseAdapter from dal import BaseAdapter
from settings import global_settings from settings import global_settings
from validators import CRYPT from validators import CRYPT
from cache import Cache from cache import CacheInRam
from html import URL, xmlescape from html import URL, xmlescape
from utils import is_valid_ip_address from utils import is_valid_ip_address
from rewrite import load, url_in, thread as rwthread, \ from rewrite import load, url_in, thread as rwthread, \
@@ -148,7 +148,7 @@ def get_client(env):
def copystream_progress(request, chunk_size= 10**5): def copystream_progress(request, chunk_size= 10**5):
""" """
copies request.env.wsgi_input into request.body copies request.env.wsgi_input into request.body
and stores progress upload status in cache.ram and stores progress upload status in cache_ram
X-Progress-ID:length and X-Progress-ID:uploaded X-Progress-ID:length and X-Progress-ID:uploaded
""" """
env = request.env env = request.env
@@ -164,16 +164,16 @@ def copystream_progress(request, chunk_size= 10**5):
copystream(source, dest, size, chunk_size) copystream(source, dest, size, chunk_size)
return dest return dest
cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID'] cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID']
cache = Cache(request) cache_ram = CacheInRam(request) # same as cache.ram because meta_storage
cache.ram(cache_key+':length', lambda: size, 0) cache_ram(cache_key+':length', lambda: size, 0)
cache.ram(cache_key+':uploaded', lambda: 0, 0) cache_ram(cache_key+':uploaded', lambda: 0, 0)
while size > 0: while size > 0:
if size < chunk_size: if size < chunk_size:
data = source.read(size) data = source.read(size)
cache.ram.increment(cache_key+':uploaded', size) cache_ram.increment(cache_key+':uploaded', size)
else: else:
data = source.read(chunk_size) data = source.read(chunk_size)
cache.ram.increment(cache_key+':uploaded', chunk_size) cache_ram.increment(cache_key+':uploaded', chunk_size)
length = len(data) length = len(data)
if length > size: if length > size:
(data, length) = (data[:size], size) (data, length) = (data[:size], size)
@@ -184,8 +184,8 @@ def copystream_progress(request, chunk_size= 10**5):
if length < chunk_size: if length < chunk_size:
break break
dest.seek(0) dest.seek(0)
cache.ram(cache_key+':length', None) cache_ram(cache_key+':length', None)
cache.ram(cache_key+':uploaded', None) cache_ram(cache_key+':uploaded', None)
return dest return dest
@@ -425,19 +425,23 @@ def wsgibase(environ, responder):
# ################################################## # ##################################################
app = request.application ## must go after url_in! app = request.application ## must go after url_in!
http_host = env.http_host.split(':',1)[0] if not global_settings.local_hosts:
local_hosts = [http_host,'::1','127.0.0.1', local_hosts = ['127.0.0.1','::ffff:127.0.0.1']
'::ffff:127.0.0.1'] if not global_settings.web2py_runtime_gae:
if not global_settings.web2py_runtime_gae: try:
try: local_hosts.append(socket.gethostname())
local_hosts.append(socket.gethostname()) except TypeError:
except TypeError: pass
pass try:
try: if env.server_name:
local_hosts.append( local_hosts += [
socket.gethostbyname(http_host)) env.server_name,
except (socket.gaierror,TypeError): socket.gethostbyname(env.server_name)]
pass except (socket.gaierror,TypeError):
pass
global_settings.local_hosts = local_hosts
else:
local_hosts = global_settings.local_hosts
client = get_client(env) client = get_client(env)
x_req_with = str(env.http_x_requested_with).lower() x_req_with = str(env.http_x_requested_with).lower()
+1 -1
View File
@@ -1466,7 +1466,7 @@ class Worker(Thread):
except UnicodeDecodeError: except UnicodeDecodeError:
self.err_log.warning('Invalid request header: '+repr(l)) self.err_log.warning('Invalid request header: '+repr(l))
if l.strip() == '': if l.strip().replace('\0','') == '':
break break
elif l[0] in ' \t' and lname: elif l[0] in ' \t' and lname:
# Some headers take more than one line # Some headers take more than one line
+1 -1
View File
@@ -6,6 +6,7 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
import os import os
import sys import sys
import socket
import platform import platform
from storage import Storage from storage import Storage
@@ -37,4 +38,3 @@ global_settings.is_jython = \
str(sys.copyright).find('Jython') > 0 str(sys.copyright).find('Jython') > 0
+1 -1
View File
@@ -1059,7 +1059,7 @@ class SQLFORM(FORM):
xfields.append((row_id,label,inp,comment)) xfields.append((row_id,label,inp,comment))
self.custom.dspval[fieldname] = dspval or nbsp self.custom.dspval[fieldname] = dspval or nbsp
self.custom.inpval[fieldname] = inpval or '' self.custom.inpval[fieldname] = inpval if not inpval is None else ''
self.custom.widget[fieldname] = inp self.custom.widget[fieldname] = inp
# if a record is provided and found, as is linkto # if a record is provided and found, as is linkto
+29 -1
View File
@@ -4576,6 +4576,7 @@ class Wiki(object):
self.env['component'] = Wiki.component self.env['component'] = Wiki.component
if render == 'markmin': render=self.markmin_render if render == 'markmin': render=self.markmin_render
elif render == 'html': render=self.html_render elif render == 'html': render=self.html_render
self.render = render
self.auth = auth self.auth = auth
if self.auth.user: if self.auth.user:
self.force_prefix = force_prefix % self.auth.user self.force_prefix = force_prefix % self.auth.user
@@ -4702,6 +4703,8 @@ class Wiki(object):
) )
elif zero=='_cloud': elif zero=='_cloud':
return self.cloud() return self.cloud()
elif zero == '_preview':
return self.preview(self.render)
def first_paragraph(self,page): def first_paragraph(self,page):
if not self.can_read(page): if not self.can_read(page):
@@ -4782,7 +4785,29 @@ class Wiki(object):
elif form.accepted: elif form.accepted:
current.session.flash = 'page created' current.session.flash = 'page created'
redirect(URL(args=slug)) redirect(URL(args=slug))
return dict(content=form) script = """
$(function() {
if (!$('#wiki_page_body').length) return;
var pagecontent = $('#wiki_page_body');
var prevbutton = $('<button class="btn nopreview">Preview</button>');
var preview = $('<div id="preview"></div>').hide();
var table = $('form');
prevbutton.insertBefore(table);
prevbutton.on('click', function(e) {
e.preventDefault();
if (prevbutton.hasClass('nopreview')) {
prevbutton.addClass('preview').removeClass('nopreview').html('Edit Source');
preview.insertBefore(table);
web2py_ajax_page('post', '%(url)s', {body : $('#wiki_page_body').val()}, 'preview');
table.fadeOut('medium', function() {preview.fadeIn()});
} else {
prevbutton.addClass('nopreview').removeClass('preview').html('Preview');
preview.fadeOut('medium', function() {table.fadeIn()});
}
})
})
""" % dict(url=URL(args=('_preview')))
return dict(content=TAG[''](form, SCRIPT(script)))
def editmedia(self,slug): def editmedia(self,slug):
auth = self.auth auth = self.auth
@@ -4977,6 +5002,9 @@ class Wiki(object):
vars=dict(q=item.wiki_tag.name)))) vars=dict(q=item.wiki_tag.name))))
items.append(' ') items.append(' ')
return dict(content = DIV(_class='w2p_cloud',*items)) return dict(content = DIV(_class='w2p_cloud',*items))
def preview(self, render):
request = current.request
return render(request.post_vars)
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
-25
View File
@@ -1,25 +0,0 @@
import os
import sys
paths = [sys.argv[1]]
paths1 = []
paths2 = []
while paths:
path = paths.pop()
for filename in os.listdir(path):
fullname = os.path.join(path,filename)
if os.path.isdir(fullname):
paths.append(fullname)
else:
extension = filename.split('.')[-1]
if extension.lower() in ('png','gif','jpg','jpeg','js','css'):
paths1.append((filename,fullname))
if extension.lower() in ('css','js','py','html'):
paths2.append(fullname)
for filename,fullname in paths1:
for otherfullname in paths2:
if open(otherfullname).read().find(filename)>=0:
break
else:
print fullname
# os.system('hg rm '+fullname)
# os.system('rm '+fullname)
+16 -7
View File
@@ -94,11 +94,11 @@ class SessionSetDb(SessionSet):
"""Return list of SessionDb instances for existing sessions.""" """Return list of SessionDb instances for existing sessions."""
sessions = [] sessions = []
tablename = 'web2py_session' tablename = 'web2py_session'
if request.application: from gluon import current
tablename = 'web2py_session_' + request.application (record_id_name, table, record_id, unique_key) = \
if tablename in db: current.response._dbtable_and_field
for row in db(db[tablename].id > 0).select(): for row in table._db(table.id > 0).select():
sessions.append(SessionDb(row)) sessions.append(SessionDb(row))
return sessions return sessions
@@ -121,8 +121,11 @@ class SessionDb(object):
self.row = row self.row = row
def delete(self): def delete(self):
from gluon import current
(record_id_name, table, record_id, unique_key) = \
current.response._dbtable_and_field
self.row.delete_record() self.row.delete_record()
db.commit() table._db.commit()
def get(self): def get(self):
session = Storage() session = Storage()
@@ -130,7 +133,13 @@ class SessionDb(object):
return session return session
def last_visit_default(self): def last_visit_default(self):
return self.row.modified_datetime if isinstance(self.row.modified_datetime, datetime.datetime):
return self.row.modified_datetime
else:
try:
return datetime.datetime.strptime(self.row.modified_datetime, '%Y-%m-%d %H:%M:%S.%f')
except:
print 'failed to retrieve last modified time (value: %s)' % self.row.modified_datetime
def __str__(self): def __str__(self):
return self.row.unique_key return self.row.unique_key