Merge github.com:web2py/web2py
This commit is contained in:
commit
aeaf0447d5
6 files changed
+153
-63
No files matched your search
@@ -1 +1 @@
|
|||||||
Version 2.00.0 (2012-07-05 21:32:06) dev
|
Version 2.00.0 (2012-07-06 08:54:40) dev
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import os
|
||||||
|
from distutils import dir_util
|
||||||
|
try:
|
||||||
|
from git import *
|
||||||
|
except ImportError:
|
||||||
|
session.flash = 'requires python-git, but not installed'
|
||||||
|
redirect(URL('default','site'))
|
||||||
|
|
||||||
|
def deploy():
|
||||||
|
apps = sorted(file for file in os.listdir(apath(r=request)))
|
||||||
|
form = SQLFORM.factory(
|
||||||
|
Field('osrepo',default='/tmp',label='Path to local openshift repo root.',
|
||||||
|
requires=EXISTS(error_message=T('directory not found'))),
|
||||||
|
Field('osname',default='web2py',label='WSGI reference name'),
|
||||||
|
Field('applications','list:string',
|
||||||
|
requires=IS_IN_SET(apps,multiple=True),
|
||||||
|
label=T('web2py apps to deploy')))
|
||||||
|
|
||||||
|
cmd = output = errors= ""
|
||||||
|
if form.accepts(request,session):
|
||||||
|
try:
|
||||||
|
kill()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
ignore_apps = [item for item in apps if not item in form.vars.applications]
|
||||||
|
regex = re.compile('\(applications/\(.*')
|
||||||
|
w2p_origin = os.getcwd()
|
||||||
|
osrepo = form.vars.osrepo
|
||||||
|
osname = form.vars.osname
|
||||||
|
#Git code starts here
|
||||||
|
repo = Repo(form.vars.osrepo)
|
||||||
|
index = repo.index
|
||||||
|
assert repo.bare == False
|
||||||
|
|
||||||
|
for i in form.vars.applications:
|
||||||
|
appsrc = os.path.join(os.getcwd(),'applications',i)
|
||||||
|
appdest = os.path.join(osrepo,'wsgi',osname,'applications',i)
|
||||||
|
dir_util.copy_tree(appsrc,appdest)
|
||||||
|
#shutil.copytree(appsrc,appdest)
|
||||||
|
index.add(['wsgi/'+osname+'/applications/'+i])
|
||||||
|
new_commit = index.commit("Deploy from Web2py IDE")
|
||||||
|
|
||||||
|
origin = repo.remotes.origin
|
||||||
|
origin.push
|
||||||
|
origin.push()
|
||||||
|
#Git code ends here
|
||||||
|
return dict(form=form,command=cmd)
|
||||||
|
|
||||||
|
class EXISTS(object):
|
||||||
|
def __init__(self, error_message='file not found'):
|
||||||
|
self.error_message = error_message
|
||||||
|
def __call__(self, value):
|
||||||
|
if os.path.exists(value):
|
||||||
|
return (value,None)
|
||||||
|
return (value,self.error_message)
|
||||||
@@ -14,10 +14,7 @@
|
|||||||
<p class="controls">
|
<p class="controls">
|
||||||
{{else:}}
|
{{else:}}
|
||||||
<h3 class="editableapp">{{=A(a,_href=URL(a,'default','index'))}}</h3>
|
<h3 class="editableapp">{{=A(a,_href=URL(a,'default','index'))}}</h3>
|
||||||
{{if MULTI_USER_MODE and db.app(name=a):}}{{owner = db.auth_user[db.app(name=a).owner]}}
|
{{if MULTI_USER_MODE and db.app(name=a):}}(created by {{="%(first_name)s %(last_name)s" % db.auth_user[db.app(name=a).owner]}}){{pass}}
|
||||||
(created by {{="%(first_name)s %(last_name)s" % owner}}
|
|
||||||
<{{=A(owner.email, _href='mailto:%s' % owner.email)}}>)
|
|
||||||
{{pass}}
|
|
||||||
<p class="controls">
|
<p class="controls">
|
||||||
{{if not os.path.exists('applications/%s/compiled' % a):}}
|
{{if not os.path.exists('applications/%s/compiled' % a):}}
|
||||||
{{=sp_button(URL('design',args=a), T("Edit"))}}
|
{{=sp_button(URL('design',args=a), T("Edit"))}}
|
||||||
@@ -39,12 +36,8 @@
|
|||||||
{{pass}}
|
{{pass}}
|
||||||
{{if a!=request.application:}}
|
{{if a!=request.application:}}
|
||||||
{{=button(URL('uninstall',args=a), T("Uninstall"))}}
|
{{=button(URL('uninstall',args=a), T("Uninstall"))}}
|
||||||
{{=button_enable(URL('enable',args=a), a)}}
|
{{=button_enable(URL('enable',args=a), a)}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
{{if a=='admin' and MULTI_USER_MODE and is_manager():}}
|
|
||||||
{{=button(URL('default','manage_students'), T('Manage Students'))}}
|
|
||||||
{{=button(URL('default','bulk_register'), T('Bulk Register'))}}
|
|
||||||
{{pass}}
|
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
{{pass}}
|
{{pass}}
|
||||||
@@ -65,19 +58,18 @@
|
|||||||
<!-- VERSION -->
|
<!-- VERSION -->
|
||||||
{{if is_manager():}}
|
{{if is_manager():}}
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3>{{=T("Version %s.%s.%s (%s) %s") % myversion}}</h3>
|
<h3>{{="Version %s.%s.%s (%s) %s" % myversion}}</h3>
|
||||||
{{if session.check_version:}}
|
{{if session.check_version:}}
|
||||||
<p id="check_version">
|
<p id="check_version">
|
||||||
{{=T('Checking for upgrades...')}}
|
{{=T('Checking for upgrades...')}}
|
||||||
<script>ajax('{{=URL('check_version')}}',[],'check_version');</script>{{session.check_version=False}}
|
<script>ajax('{{=URL('check_version')}}',[],'check_version');</script>{{session.check_version=False}}
|
||||||
</p>
|
|
||||||
{{else:}}
|
{{else:}}
|
||||||
<p id="check_version">
|
<p id="check_version">
|
||||||
{{=button("javascript:ajax('"+URL('check_version')+"',[],'check_version')", T('Check for upgrades'))}}
|
{{=button("javascript:ajax('"+URL('check_version')+"',[],'check_version')", T('Check for upgrades'))}}
|
||||||
</p>
|
|
||||||
{{pass}}
|
{{pass}}
|
||||||
|
</p>
|
||||||
<div class="formfield">
|
<div class="formfield">
|
||||||
Running on {{=request.env.server_software}}
|
Running on {{=request.env.server_software}}
|
||||||
</div>
|
</div>
|
||||||
<p>{{=button(URL('default','reload_routes'), T('Reload routes'))}}</p>
|
<p>{{=button(URL('default','reload_routes'), T('Reload routes'))}}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -105,46 +97,46 @@
|
|||||||
<h3>{{=T("Upload and install packed application")}}</h3>
|
<h3>{{=T("Upload and install packed application")}}</h3>
|
||||||
<form action="" enctype="multipart/form-data" method="post">
|
<form action="" enctype="multipart/form-data" method="post">
|
||||||
<div class="formfield">
|
<div class="formfield">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{=LABEL(T("Application name:"), _form='upload_filename')}}
|
{{=LABEL(T("Application name:"), _form='upload_filename')}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="appname" name="filename" type="text" id="upload_filename" />
|
<input id="appname" name="filename" type="text" id="upload_filename" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{=LABEL(T("Upload a package:"), _for='upload_file')}}
|
{{=LABEL(T("Upload a package:"), _for='upload_file')}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="file" name="file" type="file" id="upload_file" />
|
<input id="file" name="file" type="file" id="upload_file" />
|
||||||
<b>OR</b>
|
<b>OR</b>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{=LABEL(T("Get from URL:"), _for='upload_url')}}
|
{{=LABEL(T("Get from URL:"), _for='upload_url')}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="appurl" name="appurl" type="text" id="upload_url"/>
|
<input id="appurl" name="appurl" type="text" id="upload_url"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" name="overwrite_check" id="upload_overwrite" />
|
<input type="checkbox" name="overwrite_check" id="upload_overwrite" />
|
||||||
{{=LABEL(T("Overwrite installed app"), _for='upload_overwrite')}}
|
{{=LABEL(T("Overwrite installed app"), _for='upload_overwrite')}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
<button type="submit">{{=T('Install')}}</button>
|
<button type="submit">{{=T('Install')}}</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -153,6 +145,11 @@
|
|||||||
<h3>{{=T("Deploy on Google App Engine")}}</h3>
|
<h3>{{=T("Deploy on Google App Engine")}}</h3>
|
||||||
<p>{{=button(URL('gae','deploy'), T('Deploy'))}}</p>
|
<p>{{=button(URL('gae','deploy'), T('Deploy'))}}</p>
|
||||||
</div><br/>
|
</div><br/>
|
||||||
|
<!-- DEPLOY ON OPENSHIFT -->
|
||||||
|
<div class="box">
|
||||||
|
<h3>{{=T("Deploy to OpenShift")}}</h3>
|
||||||
|
<p>{{=button(URL('openshift','deploy'),T('Deploy'))}}</p>
|
||||||
|
</div><br/>
|
||||||
{{if TWITTER_HASH:}}
|
{{if TWITTER_HASH:}}
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3>{{=T("%s Recent Tweets"%TWITTER_HASH)}}</h3>
|
<h3>{{=T("%s Recent Tweets"%TWITTER_HASH)}}</h3>
|
||||||
@@ -164,4 +161,3 @@
|
|||||||
{{pass}}
|
{{pass}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{{extend 'layout.html'}}
|
||||||
|
<script>$=jQuery</script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{=URL(r=request,c='static',f='css/jqueryMultiSelect.css')}}" />
|
||||||
|
<script src="{{=URL(r=request,c='static',f='js/jqueryMultiSelect.js')}}"></script>
|
||||||
|
<script>
|
||||||
|
function callback() {
|
||||||
|
$.get('{{=URL(r=request,f='callback')}}','',function(data,status){ if(data!='<done/>') { $('#target').html($('#target').html()+data); callback(); } });
|
||||||
|
}
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#no_table_applications').multiSelect({selectAll: false});
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h2>OpenShift Deployment Interface</h2>
|
||||||
|
|
||||||
|
<p class="help">{{=T("This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.")}}</p>
|
||||||
|
|
||||||
|
<p class="help">{{=T("Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.")}}</p>
|
||||||
|
|
||||||
|
{{if command:}}
|
||||||
|
<h3>Command</h3>
|
||||||
|
<button onclick="$.get('{{=URL(r=request,f='kill')}}');">kill process</button>
|
||||||
|
{{=CODE(command)}}
|
||||||
|
<h3>OpenShift Output</h3>
|
||||||
|
<pre id="target"></pre>
|
||||||
|
{{else:}}
|
||||||
|
<h3>Deployment form</h3>
|
||||||
|
<div class="deploy_form form">
|
||||||
|
{{=form}}
|
||||||
|
</div>
|
||||||
|
{{pass}}
|
||||||
+17
-10
@@ -173,7 +173,7 @@ CALLABLETYPES = (types.LambdaType, types.FunctionType, types.BuiltinFunctionType
|
|||||||
|
|
||||||
|
|
||||||
###################################################################################
|
###################################################################################
|
||||||
# following checks allows running of dal without web2py as a standalone module
|
# following checks allow the use of dal without web2py, as a standalone module
|
||||||
###################################################################################
|
###################################################################################
|
||||||
try:
|
try:
|
||||||
from utils import web2py_uuid
|
from utils import web2py_uuid
|
||||||
@@ -559,7 +559,7 @@ class BaseAdapter(ConnectionPool):
|
|||||||
'list:integer': 'TEXT',
|
'list:integer': 'TEXT',
|
||||||
'list:string': 'TEXT',
|
'list:string': 'TEXT',
|
||||||
'list:reference': 'TEXT',
|
'list:reference': 'TEXT',
|
||||||
# the two below only used when DAL(...bigint_id=True) and replace 'id','reference'
|
# the two below are only used when DAL(...bigint_id=True) and replace 'id','reference'
|
||||||
'big-id': 'BIGINT PRIMARY KEY AUTOINCREMENT',
|
'big-id': 'BIGINT PRIMARY KEY AUTOINCREMENT',
|
||||||
'big-reference': 'BIGINT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
'big-reference': 'BIGINT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||||
}
|
}
|
||||||
@@ -678,7 +678,7 @@ class BaseAdapter(ConnectionPool):
|
|||||||
id_fieldname = referenced in table._db \
|
id_fieldname = referenced in table._db \
|
||||||
and table._db[referenced]._id.name or 'id'
|
and table._db[referenced]._id.name or 'id'
|
||||||
ftype = self.types[field.type[:9]] % dict(
|
ftype = self.types[field.type[:9]] % dict(
|
||||||
index_name = self.varquote(tablename+'__idx'),
|
index_name = self.varquote(field.name+'__idx'),
|
||||||
field_name = self.varquote(field.name),
|
field_name = self.varquote(field.name),
|
||||||
constraint_name = self.varquote(constraint_name),
|
constraint_name = self.varquote(constraint_name),
|
||||||
foreign_key = '%s (%s)' % (self.varquote(referenced),
|
foreign_key = '%s (%s)' % (self.varquote(referenced),
|
||||||
@@ -724,6 +724,8 @@ class BaseAdapter(ConnectionPool):
|
|||||||
ftype += self.ALLOW_NULL()
|
ftype += self.ALLOW_NULL()
|
||||||
if field.unique:
|
if field.unique:
|
||||||
ftype += ' UNIQUE'
|
ftype += ' UNIQUE'
|
||||||
|
if field.custom_qualifier:
|
||||||
|
ftype += ' %s' % field.custom_qualifier
|
||||||
|
|
||||||
# add to list of fields
|
# add to list of fields
|
||||||
sql_fields[field.name] = dict(sortable=sortable,
|
sql_fields[field.name] = dict(sortable=sortable,
|
||||||
@@ -1484,6 +1486,8 @@ class BaseAdapter(ConnectionPool):
|
|||||||
|
|
||||||
def log_execute(self, *a, **b):
|
def log_execute(self, *a, **b):
|
||||||
command = a[0]
|
command = a[0]
|
||||||
|
if self.db._debug:
|
||||||
|
logger.debug('SQL: %s' % command)
|
||||||
self.db._lastsql = command
|
self.db._lastsql = command
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
ret = self.cursor.execute(*a, **b)
|
ret = self.cursor.execute(*a, **b)
|
||||||
@@ -1917,7 +1921,7 @@ class SpatiaLiteAdapter(SQLiteAdapter):
|
|||||||
|
|
||||||
def after_connection(self):
|
def after_connection(self):
|
||||||
self.connection.enable_load_extension(True)
|
self.connection.enable_load_extension(True)
|
||||||
# for Windows, rename libspatialite-2.dll as libspatialite.dll
|
# for Windows, rename libspatialite-2.dll to libspatialite.dll
|
||||||
# Linux uses libspatialite.so
|
# Linux uses libspatialite.so
|
||||||
# Mac OS X uses libspatialite.dylib
|
# Mac OS X uses libspatialite.dylib
|
||||||
libspatialite = SPATIALLIBS[platform.system()]
|
libspatialite = SPATIALLIBS[platform.system()]
|
||||||
@@ -2041,7 +2045,7 @@ class MySQLAdapter(BaseAdapter):
|
|||||||
'list:string': 'LONGTEXT',
|
'list:string': 'LONGTEXT',
|
||||||
'list:reference': 'LONGTEXT',
|
'list:reference': 'LONGTEXT',
|
||||||
'big-id': 'BIGINT AUTO_INCREMENT NOT NULL',
|
'big-id': 'BIGINT AUTO_INCREMENT NOT NULL',
|
||||||
'big-reference': 'BIGINT, INDEX %(index_name)s (%(field_name)s), FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
'big-reference': 'BIGINT, INDEX %(index_name`)s (%(field_name)s), FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s',
|
||||||
}
|
}
|
||||||
|
|
||||||
def varquote(self,name):
|
def varquote(self,name):
|
||||||
@@ -4280,7 +4284,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
|
|||||||
"""
|
"""
|
||||||
This function was changed on 2010-05-04 because according to
|
This function was changed on 2010-05-04 because according to
|
||||||
http://code.google.com/p/googleappengine/issues/detail?id=3119
|
http://code.google.com/p/googleappengine/issues/detail?id=3119
|
||||||
GAE no longer support deleting more than 1000 records.
|
GAE no longer supports deleting more than 1000 records.
|
||||||
"""
|
"""
|
||||||
# self.db['_lastsql'] = self._delete(tablename,query)
|
# self.db['_lastsql'] = self._delete(tablename,query)
|
||||||
(items, tablename, fields) = self.select_raw(query)
|
(items, tablename, fields) = self.select_raw(query)
|
||||||
@@ -4664,7 +4668,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
|||||||
if expression.second != 0 and not isinstance(expression.second,pymongo.objectid.ObjectId):
|
if expression.second != 0 and not isinstance(expression.second,pymongo.objectid.ObjectId):
|
||||||
if isinstance(expression.second,int):
|
if isinstance(expression.second,int):
|
||||||
try:
|
try:
|
||||||
#Cause the reference field is by default an integer and therefor this must be an integer to be able to work with other databases
|
#Because the reference field is by default an integer and therefore this must be an integer to be able to work with other databases
|
||||||
expression.second = pymongo.objectid.ObjectId(("%X" % expression.second))
|
expression.second = pymongo.objectid.ObjectId(("%X" % expression.second))
|
||||||
except:
|
except:
|
||||||
raise SyntaxError, 'The second argument must by an integer that can represent an objectid.'
|
raise SyntaxError, 'The second argument must by an integer that can represent an objectid.'
|
||||||
@@ -6218,8 +6222,8 @@ copy_reg.pickle(Row, Row_pickler, Row_unpickler)
|
|||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Everything below should be independent on the specifics of the
|
# Everything below should be independent of the specifics of the database
|
||||||
# database and should for RDBMs and some NoSQL databases
|
# and should work for RDBMs and some NoSQL databases
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
class SQLCallableList(list):
|
class SQLCallableList(list):
|
||||||
@@ -6403,7 +6407,7 @@ class DAL(dict):
|
|||||||
migrate_enabled=True, fake_migrate_all=False,
|
migrate_enabled=True, fake_migrate_all=False,
|
||||||
decode_credentials=False, driver_args=None,
|
decode_credentials=False, driver_args=None,
|
||||||
adapter_args=None, attempts=5, auto_import=False,
|
adapter_args=None, attempts=5, auto_import=False,
|
||||||
bigint_id=False):
|
bigint_id=False,debug=False):
|
||||||
"""
|
"""
|
||||||
Creates a new Database Abstraction Layer instance.
|
Creates a new Database Abstraction Layer instance.
|
||||||
|
|
||||||
@@ -6446,6 +6450,7 @@ class DAL(dict):
|
|||||||
self._common_fields = []
|
self._common_fields = []
|
||||||
self._referee_name = '%(table)s'
|
self._referee_name = '%(table)s'
|
||||||
self._bigint_id = bigint_id
|
self._bigint_id = bigint_id
|
||||||
|
self._debug = debug
|
||||||
if not str(attempts).isdigit() or attempts < 0:
|
if not str(attempts).isdigit() or attempts < 0:
|
||||||
attempts = 5
|
attempts = 5
|
||||||
if uri:
|
if uri:
|
||||||
@@ -7962,6 +7967,7 @@ class Field(Expression):
|
|||||||
custom_delete=None,
|
custom_delete=None,
|
||||||
filter_in = None,
|
filter_in = None,
|
||||||
filter_out = None,
|
filter_out = None,
|
||||||
|
custom_qualifier = None,
|
||||||
):
|
):
|
||||||
self.db = None
|
self.db = None
|
||||||
self.op = None
|
self.op = None
|
||||||
@@ -8011,6 +8017,7 @@ class Field(Expression):
|
|||||||
self.custom_delete = custom_delete
|
self.custom_delete = custom_delete
|
||||||
self.filter_in = filter_in
|
self.filter_in = filter_in
|
||||||
self.filter_out = filter_out
|
self.filter_out = filter_out
|
||||||
|
self.custom_qualifier = custom_qualifier
|
||||||
if self.label is None:
|
if self.label is None:
|
||||||
self.label = fieldname.replace('_',' ').title()
|
self.label = fieldname.replace('_',' ').title()
|
||||||
if requires is None:
|
if requires is None:
|
||||||
|
|||||||
+1
-2
@@ -329,7 +329,6 @@ class Response(Storage):
|
|||||||
downloads from http://..../download/filename
|
downloads from http://..../download/filename
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import contenttype as c
|
|
||||||
if not request.args:
|
if not request.args:
|
||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
name = request.args[-1]
|
name = request.args[-1]
|
||||||
@@ -343,7 +342,7 @@ class Response(Storage):
|
|||||||
(filename, stream) = field.retrieve(name)
|
(filename, stream) = field.retrieve(name)
|
||||||
except IOError:
|
except IOError:
|
||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
self.headers['Content-Type'] = c.contenttype(name)
|
self.headers['Content-Type'] = contenttype(name)
|
||||||
if attachment:
|
if attachment:
|
||||||
self.headers['Content-Disposition'] = \
|
self.headers['Content-Disposition'] = \
|
||||||
"attachment; filename=%s" % filename
|
"attachment; filename=%s" % filename
|
||||||
|
|||||||
Reference in new issue
Block a user