├── .gitignore ├── README ├── dev_server ├── README ├── bundle_local.py ├── cloudsql.py ├── dev_server.py ├── sae │ ├── __init__.py │ ├── _restful_mysql │ │ ├── __init__.py │ │ ├── _mysql.py │ │ ├── _mysql_exceptions.py │ │ ├── connections.py │ │ ├── constants │ │ │ ├── CLIENT.py │ │ │ ├── CR.py │ │ │ ├── ER.py │ │ │ ├── FIELD_TYPE.py │ │ │ ├── FLAG.py │ │ │ ├── REFRESH.py │ │ │ └── __init__.py │ │ ├── converters.py │ │ ├── cursors.py │ │ ├── monkey.py │ │ ├── release.py │ │ └── times.py │ ├── channel.js │ ├── channel.py │ ├── channel.src.js │ ├── conf.py │ ├── const.py │ ├── core.py │ ├── ext │ │ ├── __init__.py │ │ ├── django │ │ │ ├── __init__.py │ │ │ ├── mail │ │ │ │ ├── __init__.py │ │ │ │ └── backend.py │ │ │ └── storage │ │ │ │ ├── __init__.py │ │ │ │ └── backend.py │ │ ├── shell.py │ │ └── storage │ │ │ ├── __init__.py │ │ │ └── monkey.py │ ├── kvdb.py │ ├── mail.py │ ├── memcache.py │ ├── sae_signature.py │ ├── storage.py │ ├── taskqueue.py │ └── util.py ├── saecloud └── setup.py ├── docs ├── Makefile ├── conf.py ├── exts │ └── chinese_search.py ├── faq.rst ├── index.rst ├── quickstart.rst ├── runtime.rst ├── service.rst ├── static │ ├── channel-overview.png │ └── memcache.html ├── theme │ └── nature │ │ ├── layout.html │ │ ├── static │ │ ├── comment_white_yellow.gif │ │ ├── nature.css_t │ │ └── sae.js │ │ └── theme.conf └── tools.rst └── examples ├── apibus └── apibus_handler.py ├── bottle ├── README └── index.wsgi ├── django ├── 1.2.7 │ ├── README │ ├── config.yaml │ ├── index.wsgi │ ├── mysite │ │ ├── __init__.py │ │ ├── demo │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ ├── tests.py │ │ │ └── views.py │ │ ├── manage.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── views.py │ └── static │ │ ├── css │ │ ├── base.css │ │ ├── changelists.css │ │ ├── dashboard.css │ │ ├── forms.css │ │ ├── ie.css │ │ ├── login.css │ │ ├── rtl.css │ │ └── widgets.css │ │ ├── img │ │ ├── admin │ │ │ ├── arrow-down.gif │ │ │ ├── arrow-up.gif │ │ │ ├── changelist-bg.gif │ │ │ ├── changelist-bg_rtl.gif │ │ │ ├── chooser-bg.gif │ │ │ ├── chooser_stacked-bg.gif │ │ │ ├── default-bg-reverse.gif │ │ │ ├── default-bg.gif │ │ │ ├── deleted-overlay.gif │ │ │ ├── icon-no.gif │ │ │ ├── icon-unknown.gif │ │ │ ├── icon-yes.gif │ │ │ ├── icon_addlink.gif │ │ │ ├── icon_alert.gif │ │ │ ├── icon_calendar.gif │ │ │ ├── icon_changelink.gif │ │ │ ├── icon_clock.gif │ │ │ ├── icon_deletelink.gif │ │ │ ├── icon_error.gif │ │ │ ├── icon_searchbox.png │ │ │ ├── icon_success.gif │ │ │ ├── inline-delete-8bit.png │ │ │ ├── inline-delete.png │ │ │ ├── inline-restore-8bit.png │ │ │ ├── inline-restore.png │ │ │ ├── inline-splitter-bg.gif │ │ │ ├── nav-bg-grabber.gif │ │ │ ├── nav-bg-reverse.gif │ │ │ ├── nav-bg.gif │ │ │ ├── selector-add.gif │ │ │ ├── selector-addall.gif │ │ │ ├── selector-remove.gif │ │ │ ├── selector-removeall.gif │ │ │ ├── selector-search.gif │ │ │ ├── selector_stacked-add.gif │ │ │ ├── selector_stacked-remove.gif │ │ │ ├── tool-left.gif │ │ │ ├── tool-left_over.gif │ │ │ ├── tool-right.gif │ │ │ ├── tool-right_over.gif │ │ │ ├── tooltag-add.gif │ │ │ ├── tooltag-add_over.gif │ │ │ ├── tooltag-arrowright.gif │ │ │ └── tooltag-arrowright_over.gif │ │ └── gis │ │ │ ├── move_vertex_off.png │ │ │ └── move_vertex_on.png │ │ └── js │ │ ├── LICENSE-JQUERY.txt │ │ ├── SelectBox.js │ │ ├── SelectFilter2.js │ │ ├── actions.js │ │ ├── actions.min.js │ │ ├── admin │ │ ├── DateTimeShortcuts.js │ │ ├── RelatedObjectLookups.js │ │ └── ordering.js │ │ ├── calendar.js │ │ ├── collapse.js │ │ ├── collapse.min.js │ │ ├── compress.py │ │ ├── core.js │ │ ├── dateparse.js │ │ ├── getElementsBySelector.js │ │ ├── inlines.js │ │ ├── inlines.min.js │ │ ├── jquery.init.js │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ ├── prepopulate.js │ │ ├── prepopulate.min.js │ │ ├── timeparse.js │ │ └── urlify.js └── 1.4 │ ├── README │ ├── config.yaml │ ├── db.sql │ ├── index.wsgi │ ├── manage.py │ ├── mysite │ ├── __init__.py │ ├── settings.py │ ├── templates │ │ ├── 404.html │ │ └── 500.html │ ├── urls.py │ └── wsgi.py │ └── polls │ ├── __init__.py │ ├── admin.py │ ├── models.py │ ├── templates │ ├── detail.html │ ├── index.html │ └── results.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── flask ├── README ├── app.py ├── index.wsgi └── myapp.py ├── helloworld └── 1 │ └── index.wsgi ├── matplotshell ├── README ├── config.yaml └── index.wsgi ├── renren ├── config.yaml ├── db.sql ├── error.html ├── index.wsgi ├── oauth.html └── renrenoauth.py ├── segment ├── config.yaml └── index.wsgi ├── static-site ├── README ├── config.yaml └── www │ ├── appengine_button_noborder.gif │ └── index.html ├── tornado ├── async │ ├── config.yaml │ └── index.wsgi └── wsgi │ ├── README │ └── index.wsgi ├── trac ├── README.md ├── config.yaml ├── index.wsgi ├── project │ ├── README │ ├── VERSION │ ├── conf │ │ ├── trac.ini │ │ └── trac.ini.sample │ └── templates │ │ └── site.html.sample ├── requirements.txt └── setup.sql ├── webpy ├── index.wsgi └── templates │ └── hello.html └── weibo ├── appstack.py └── index.wsgi /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | docs/_build/* 3 | dev_server/build/* 4 | dev_server/sae_python_dev.egg-info/* 5 | *~ 6 | *.DS_Store 7 | *.default 8 | *.types 9 | *.pyc 10 | *.egg 11 | *.pdf 12 | *.bak 13 | *.doc 14 | *.ppt 15 | *.xls 16 | *.mht 17 | *.mp3 18 | *.mp4 19 | *.wma 20 | *.rar 21 | *.zip 22 | *.7z 23 | *.xz 24 | *.map 25 | *.log 26 | *.txt 27 | *.swp 28 | *.gz 29 | *.ko 30 | *.so 31 | .tmp* 32 | .svn 33 | .cvs 34 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Copyright © 2011 新浪网研发中心. All rights reserved. 2 | 3 | SAE Python Team 4 | -------------------------------------------------------------------------------- /dev_server/README: -------------------------------------------------------------------------------- 1 | SAE Python development server - experimental 2 | 3 | All Rights Reserved 2011 4 | SAE Python Team 5 | 6 | 目前支持的服务包括:mysql, taskqueue, memcache, storage, mail。 7 | 大部分的服务直接运行dev_server.py进行调试就可以了,部分服务需要做一些配置。 8 | 9 | 注意: 本工具仅为应用开发便利之用,对sae python环境的模拟并不完整。 10 | 11 | Install 12 | -------------- 13 | 14 | sudo python setup.py install 15 | 16 | 基本使用 17 | ------------ 18 | 19 | 使用svn检出app代码之后,建立以数字为标识的发布目录,切换到发布目录: 20 | 21 | $ pwd 22 | /home/jaime/source/blackfire/1 23 | 24 | 编辑index.wsgi和config.yaml: 25 | 26 | $ vi index.wsgi 27 | import sae 28 | 29 | def app(environ, start_response): 30 | status = '200 OK' 31 | response_headers = [('Content-type', 'text/plain')] 32 | start_response(status, response_headers) 33 | return ['Hello, world! reloading test3'] 34 | 35 | application = sae.create_wsgi_app(app) 36 | 37 | $ vi config.yaml 38 | --- 39 | name: blackfire 40 | version: 1 41 | ... 42 | 43 | 运行dev_server.py: 44 | 45 | $ dev_server.py 46 | MySQL config not found: app.py 47 | Start development server on http://localhost:8080/ 48 | 49 | 访问 http://localhost:8080 端口就可以看到Hello, world!了。 50 | 51 | 使用MySQL服务 52 | ---------------- 53 | 54 | 配置好MySQL本地开发server,使用 `--mysql` 参数运行dev_server.py。 55 | 56 | $ dev_server.py --mysql=user:password@host:port 57 | 58 | 现在你可以在应用代码中像在SAE线上环境一样使用MySQL服务了。dev_server.py默认使用 59 | 名为 `app_应用名` 的数据库。 60 | 61 | 使用storage服务 62 | --------------- 63 | 64 | 使用 `--storage-path` 参数运行dev_server.py。 65 | 66 | $ dev_server.py --storage-path=/path/to/local/storage/data 67 | 68 | 本地的storage服务使用以下的目录结构来模拟线上的storage。 69 | 70 | storage-path/ 71 | domain1/ 72 | key1 73 | key2 74 | domain2/ 75 | domain3/ 76 | 77 | --storage-path配置的路径下每个子文件夹会映射为storage中的一个domain,而每个子文 78 | 件夹下的文件映射为domain下的一个key,其内容为对应key的数据。 79 | 80 | .. note: 81 | 82 | 为方便调试,dev_server自带的sae.storage在某个domain不存在的情况下会自动创建 83 | 该domain。线上环境中的domain需要在sae后台面板中手动创建。 84 | 85 | 使用pylibmc 86 | -------------- 87 | 88 | dev_server自带了一个dummy pylibmc,所以无须安装pylibmc就可以直接使用memcache服务 89 | 了。该模块将所有的数据存贮在内存中,dev_server.py进程结束时,所有的数据都会丢失 90 | 。 91 | 92 | 使用kvdb 93 | ---------------- 94 | 95 | kvdb默认数据存在内存中,dev_server.py进程结束时,数据会全部丢失,如果需要保存数据, 96 | 请使用如下命令行启动dev_server.py 97 | 98 | $ dev_server.py --kvdb-file=/path/to/kvdb/local/file 99 | -------------------------------------------------------------------------------- /dev_server/bundle_local.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Create a local bundle for your virtualenv environment 4 | 5 | Export all the packages listed in requirements.txt to ./virtualenv.bundle 6 | directory 7 | 8 | Then you can create a .zip file before uploading them to sae 9 | 10 | Usage: bundle_local -r requirements.txt 11 | 12 | """ 13 | 14 | from optparse import OptionParser 15 | import os 16 | import sys 17 | import pip.util 18 | import shutil 19 | 20 | TMP = 'virtualenv.bundle' 21 | 22 | ZIP_FILE = TMP + '.zip' 23 | 24 | def main(): 25 | parser = OptionParser() 26 | parser.add_option("-r", dest="requirements", 27 | help="The requirements.txt file outputed by pip freeze") 28 | (options, args) = parser.parse_args() 29 | 30 | if not options.requirements: 31 | print 'requirements.txt not found' 32 | sys.exit(-1) 33 | 34 | if os.path.exists(TMP): 35 | shutil.rmtree(TMP) 36 | os.mkdir(TMP) 37 | 38 | shutil.copy2(options.requirements, os.path.join(TMP, 'requirements.txt')) 39 | 40 | # Get all installed packages on system 41 | installed_dists = {} 42 | for dist in pip.util.get_installed_distributions(): 43 | installed_dists[dist.project_name] = dist 44 | 45 | # Get the dists in requirements.txt 46 | dists = [] 47 | for line in open(options.requirements, 'r').readlines(): 48 | if line.strip() or line.startswith('#'): 49 | pass 50 | pkg = line.split('==')[0] 51 | 52 | if pkg not in installed_dists: 53 | raise Exception('%s not installed' % pkg) 54 | dists.append(installed_dists[pkg]) 55 | 56 | top_levels = [] 57 | for dist in dists: 58 | mods = [(dist.location, mod) for mod in dist.get_metadata('top_level.txt').splitlines()] 59 | top_levels += mods 60 | 61 | top_levels = list(set(top_levels)) 62 | copy_modules(top_levels, TMP) 63 | 64 | 65 | def copy_modules(mod_paths, dest): 66 | for loc, mod in mod_paths: 67 | if os.path.isdir(loc): 68 | src = os.path.join(loc, mod) 69 | if os.path.isdir(src): 70 | shutil.copytree(src, os.path.join(dest, mod), ignore=shutil.ignore_patterns('*.pyc')) 71 | else: 72 | # Single file module 73 | shutil.copy2(src + '.py', dest) 74 | else: 75 | # Egg file ? 76 | import zipfile 77 | zf = zipfile.ZipFile(loc) 78 | members = filter(lambda f: f.startswith(mod), zf.namelist()) 79 | for m in members: 80 | zf.extract(m, dest) 81 | zf.close() 82 | 83 | if __name__ == '__main__': 84 | main() 85 | -------------------------------------------------------------------------------- /dev_server/sae/__init__.py: -------------------------------------------------------------------------------- 1 | """SAE Python 2 | 3 | Jaime Chen 2011 4 | """ 5 | 6 | import core 7 | 8 | def create_wsgi_app(app): 9 | return app 10 | 11 | def dev_server(conf): 12 | core.environ = conf 13 | 14 | import sys, os.path 15 | _PYTHON_VERSION = 'python%d.%d' % (sys.version_info[0], sys.version_info[1]) 16 | 17 | def add_vendor_dir(path, index=1): 18 | """Insert site dir or virtualenv at a given index in sys.path. 19 | 20 | Args: 21 | path: relative path to a site dir or virtualenv. 22 | index: sys.path position to insert the site dir. 23 | 24 | Raises: 25 | ValueError: path doesn't exist. 26 | """ 27 | venv_path = os.path.join(path, 'lib', _PYTHON_VERSION, 'site-packages') 28 | if os.path.isdir(venv_path): 29 | site_dir = venv_path 30 | elif os.path.isdir(path): 31 | site_dir = path 32 | else: 33 | raise ValueError('virtualenv: cannot access %s: ' 34 | 'No such virtualenv or site directory' % path) 35 | 36 | sys_path = sys.path[:] 37 | del sys.path[index:] 38 | import site 39 | site.addsitedir(site_dir) 40 | sys.path.extend(sys_path[index:]) 41 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/__init__.py: -------------------------------------------------------------------------------- 1 | """MySQLdb - A DB API v2.0 compatible interface to MySQL. 2 | 3 | This package is a wrapper around _mysql, which mostly implements the 4 | MySQL C API. 5 | 6 | connect() -- connects to server 7 | 8 | See the C API specification and the MySQL documentation for more info 9 | on other items. 10 | 11 | For information on how MySQLdb handles type conversion, see the 12 | MySQLdb.converters module. 13 | 14 | """ 15 | 16 | __revision__ = """$Revision: 603 $"""[11:-2] 17 | from release import __version__, version_info, __author__ 18 | 19 | import _mysql 20 | 21 | if version_info != _mysql.version_info: 22 | raise ImportError("this is MySQLdb version %s, but _mysql is version %r" % 23 | (version_info, _mysql.version_info)) 24 | 25 | threadsafety = 1 26 | apilevel = "2.0" 27 | paramstyle = "format" 28 | 29 | from _mysql import * 30 | from constants import FIELD_TYPE 31 | from times import Date, Time, Timestamp, \ 32 | DateFromTicks, TimeFromTicks, TimestampFromTicks 33 | 34 | try: 35 | frozenset 36 | except NameError: 37 | from sets import ImmutableSet as frozenset 38 | 39 | class DBAPISet(frozenset): 40 | 41 | """A special type of set for which A == x is true if A is a 42 | DBAPISet and x is a member of that set.""" 43 | 44 | def __eq__(self, other): 45 | if isinstance(other, DBAPISet): 46 | return not self.difference(other) 47 | return other in self 48 | 49 | 50 | STRING = DBAPISet([FIELD_TYPE.ENUM, FIELD_TYPE.STRING, 51 | FIELD_TYPE.VAR_STRING]) 52 | BINARY = DBAPISet([FIELD_TYPE.BLOB, FIELD_TYPE.LONG_BLOB, 53 | FIELD_TYPE.MEDIUM_BLOB, FIELD_TYPE.TINY_BLOB]) 54 | NUMBER = DBAPISet([FIELD_TYPE.DECIMAL, FIELD_TYPE.DOUBLE, FIELD_TYPE.FLOAT, 55 | FIELD_TYPE.INT24, FIELD_TYPE.LONG, FIELD_TYPE.LONGLONG, 56 | FIELD_TYPE.TINY, FIELD_TYPE.YEAR]) 57 | DATE = DBAPISet([FIELD_TYPE.DATE, FIELD_TYPE.NEWDATE]) 58 | TIME = DBAPISet([FIELD_TYPE.TIME]) 59 | TIMESTAMP = DBAPISet([FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME]) 60 | DATETIME = TIMESTAMP 61 | ROWID = DBAPISet() 62 | 63 | def test_DBAPISet_set_equality(): 64 | assert STRING == STRING 65 | 66 | def test_DBAPISet_set_inequality(): 67 | assert STRING != NUMBER 68 | 69 | def test_DBAPISet_set_equality_membership(): 70 | assert FIELD_TYPE.VAR_STRING == STRING 71 | 72 | def test_DBAPISet_set_inequality_membership(): 73 | assert FIELD_TYPE.DATE != STRING 74 | 75 | def Binary(x): 76 | return str(x) 77 | 78 | def Connect(*args, **kwargs): 79 | """Factory function for connections.Connection.""" 80 | from connections import Connection 81 | return Connection(*args, **kwargs) 82 | 83 | connect = Connection = Connect 84 | 85 | __all__ = [ 'BINARY', 'Binary', 'Connect', 'Connection', 'DATE', 86 | 'Date', 'Time', 'Timestamp', 'DateFromTicks', 'TimeFromTicks', 87 | 'TimestampFromTicks', 'DataError', 'DatabaseError', 'Error', 88 | 'FIELD_TYPE', 'IntegrityError', 'InterfaceError', 'InternalError', 89 | 'MySQLError', 'NULL', 'NUMBER', 'NotSupportedError', 'DBAPISet', 90 | 'OperationalError', 'ProgrammingError', 'ROWID', 'STRING', 'TIME', 91 | 'TIMESTAMP', 'Warning', 'apilevel', 'connect', 'connections', 92 | 'constants', 'converters', 'cursors', 'debug', 'escape', 'escape_dict', 93 | 'escape_sequence', 'escape_string', 'get_client_info', 94 | 'paramstyle', 'string_literal', 'threadsafety', 'version_info'] 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/_mysql_exceptions.py: -------------------------------------------------------------------------------- 1 | """_mysql_exceptions: Exception classes for _mysql and MySQLdb. 2 | 3 | These classes are dictated by the DB API v2.0: 4 | 5 | http://www.python.org/topics/database/DatabaseAPI-2.0.html 6 | """ 7 | 8 | from exceptions import Exception, StandardError, Warning 9 | 10 | class MySQLError(StandardError): 11 | 12 | """Exception related to operation with MySQL.""" 13 | 14 | 15 | class Warning(Warning, MySQLError): 16 | 17 | """Exception raised for important warnings like data truncations 18 | while inserting, etc.""" 19 | 20 | class Error(MySQLError): 21 | 22 | """Exception that is the base class of all other error exceptions 23 | (not Warning).""" 24 | 25 | 26 | class InterfaceError(Error): 27 | 28 | """Exception raised for errors that are related to the database 29 | interface rather than the database itself.""" 30 | 31 | 32 | class DatabaseError(Error): 33 | 34 | """Exception raised for errors that are related to the 35 | database.""" 36 | 37 | 38 | class DataError(DatabaseError): 39 | 40 | """Exception raised for errors that are due to problems with the 41 | processed data like division by zero, numeric value out of range, 42 | etc.""" 43 | 44 | 45 | class OperationalError(DatabaseError): 46 | 47 | """Exception raised for errors that are related to the database's 48 | operation and not necessarily under the control of the programmer, 49 | e.g. an unexpected disconnect occurs, the data source name is not 50 | found, a transaction could not be processed, a memory allocation 51 | error occurred during processing, etc.""" 52 | 53 | 54 | class IntegrityError(DatabaseError): 55 | 56 | """Exception raised when the relational integrity of the database 57 | is affected, e.g. a foreign key check fails, duplicate key, 58 | etc.""" 59 | 60 | 61 | class InternalError(DatabaseError): 62 | 63 | """Exception raised when the database encounters an internal 64 | error, e.g. the cursor is not valid anymore, the transaction is 65 | out of sync, etc.""" 66 | 67 | 68 | class ProgrammingError(DatabaseError): 69 | 70 | """Exception raised for programming errors, e.g. table not found 71 | or already exists, syntax error in the SQL statement, wrong number 72 | of parameters specified, etc.""" 73 | 74 | 75 | class NotSupportedError(DatabaseError): 76 | 77 | """Exception raised in case a method or database API was used 78 | which is not supported by the database, e.g. requesting a 79 | .rollback() on a connection that does not support transaction or 80 | has transactions turned off.""" 81 | 82 | 83 | del Exception, StandardError 84 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/constants/CLIENT.py: -------------------------------------------------------------------------------- 1 | """MySQL CLIENT constants 2 | 3 | These constants are used when creating the connection. Use bitwise-OR 4 | (|) to combine options together, and pass them as the client_flags 5 | parameter to MySQLdb.Connection. For more information on these flags, 6 | see the MySQL C API documentation for mysql_real_connect(). 7 | 8 | """ 9 | 10 | LONG_PASSWORD = 1 11 | FOUND_ROWS = 2 12 | LONG_FLAG = 4 13 | CONNECT_WITH_DB = 8 14 | NO_SCHEMA = 16 15 | COMPRESS = 32 16 | ODBC = 64 17 | LOCAL_FILES = 128 18 | IGNORE_SPACE = 256 19 | CHANGE_USER = 512 20 | INTERACTIVE = 1024 21 | SSL = 2048 22 | IGNORE_SIGPIPE = 4096 23 | TRANSACTIONS = 8192 # mysql_com.h was WRONG prior to 3.23.35 24 | RESERVED = 16384 25 | SECURE_CONNECTION = 32768 26 | MULTI_STATEMENTS = 65536 27 | MULTI_RESULTS = 131072 28 | 29 | 30 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/constants/CR.py: -------------------------------------------------------------------------------- 1 | """MySQL Connection Errors 2 | 3 | Nearly all of these raise OperationalError. COMMANDS_OUT_OF_SYNC 4 | raises ProgrammingError. 5 | 6 | """ 7 | 8 | MIN_ERROR = 2000 9 | MAX_ERROR = 2999 10 | UNKNOWN_ERROR = 2000 11 | SOCKET_CREATE_ERROR = 2001 12 | CONNECTION_ERROR = 2002 13 | CONN_HOST_ERROR = 2003 14 | IPSOCK_ERROR = 2004 15 | UNKNOWN_HOST = 2005 16 | SERVER_GONE_ERROR = 2006 17 | VERSION_ERROR = 2007 18 | OUT_OF_MEMORY = 2008 19 | WRONG_HOST_INFO = 2009 20 | LOCALHOST_CONNECTION = 2010 21 | TCP_CONNECTION = 2011 22 | SERVER_HANDSHAKE_ERR = 2012 23 | SERVER_LOST = 2013 24 | COMMANDS_OUT_OF_SYNC = 2014 25 | NAMEDPIPE_CONNECTION = 2015 26 | NAMEDPIPEWAIT_ERROR = 2016 27 | NAMEDPIPEOPEN_ERROR = 2017 28 | NAMEDPIPESETSTATE_ERROR = 2018 29 | CANT_READ_CHARSET = 2019 30 | NET_PACKET_TOO_LARGE = 2020 31 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/constants/FIELD_TYPE.py: -------------------------------------------------------------------------------- 1 | """MySQL FIELD_TYPE Constants 2 | 3 | These constants represent the various column (field) types that are 4 | supported by MySQL. 5 | 6 | """ 7 | 8 | DECIMAL = 0 9 | TINY = 1 10 | SHORT = 2 11 | LONG = 3 12 | FLOAT = 4 13 | DOUBLE = 5 14 | NULL = 6 15 | TIMESTAMP = 7 16 | LONGLONG = 8 17 | INT24 = 9 18 | DATE = 10 19 | TIME = 11 20 | DATETIME = 12 21 | YEAR = 13 22 | NEWDATE = 14 23 | VARCHAR = 15 24 | BIT = 16 25 | NEWDECIMAL = 246 26 | ENUM = 247 27 | SET = 248 28 | TINY_BLOB = 249 29 | MEDIUM_BLOB = 250 30 | LONG_BLOB = 251 31 | BLOB = 252 32 | VAR_STRING = 253 33 | STRING = 254 34 | GEOMETRY = 255 35 | 36 | CHAR = TINY 37 | INTERVAL = ENUM 38 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/constants/FLAG.py: -------------------------------------------------------------------------------- 1 | """MySQL FLAG Constants 2 | 3 | These flags are used along with the FIELD_TYPE to indicate various 4 | properties of columns in a result set. 5 | 6 | """ 7 | 8 | NOT_NULL = 1 9 | PRI_KEY = 2 10 | UNIQUE_KEY = 4 11 | MULTIPLE_KEY = 8 12 | BLOB = 16 13 | UNSIGNED = 32 14 | ZEROFILL = 64 15 | BINARY = 128 16 | ENUM = 256 17 | AUTO_INCREMENT = 512 18 | TIMESTAMP = 1024 19 | SET = 2048 20 | NUM = 32768 21 | PART_KEY = 16384 22 | GROUP = 32768 23 | UNIQUE = 65536 24 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/constants/REFRESH.py: -------------------------------------------------------------------------------- 1 | """MySQL REFRESH Constants 2 | 3 | These constants seem to mostly deal with things internal to the 4 | MySQL server. Forget you saw this. 5 | 6 | """ 7 | 8 | GRANT = 1 9 | LOG = 2 10 | TABLES = 4 11 | HOSTS = 8 12 | STATUS = 16 13 | THREADS = 32 14 | SLAVE = 64 15 | MASTER = 128 16 | READ_LOCK = 16384 17 | FAST = 32768 18 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/constants/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['CR', 'FIELD_TYPE','CLIENT','REFRESH','ER','FLAG'] 2 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/converters.py: -------------------------------------------------------------------------------- 1 | """MySQLdb type conversion module 2 | 3 | This module handles all the type conversions for MySQL. If the default 4 | type conversions aren't what you need, you can make your own. The 5 | dictionary conversions maps some kind of type to a conversion function 6 | which returns the corresponding value: 7 | 8 | Key: FIELD_TYPE.* (from MySQLdb.constants) 9 | 10 | Conversion function: 11 | 12 | Arguments: string 13 | 14 | Returns: Python object 15 | 16 | Key: Python type object (from types) or class 17 | 18 | Conversion function: 19 | 20 | Arguments: Python object of indicated type or class AND 21 | conversion dictionary 22 | 23 | Returns: SQL literal value 24 | 25 | Notes: Most conversion functions can ignore the dictionary, but 26 | it is a required parameter. It is necessary for converting 27 | things like sequences and instances. 28 | 29 | Don't modify conversions if you can avoid it. Instead, make copies 30 | (with the copy() method), modify the copies, and then pass them to 31 | MySQL.connect(). 32 | 33 | """ 34 | 35 | from _mysql import string_literal, escape_sequence, escape_dict, escape, NULL 36 | from constants import FIELD_TYPE, FLAG 37 | from times import * 38 | import types 39 | import array 40 | 41 | try: 42 | set 43 | except NameError: 44 | from sets import Set as set 45 | 46 | def Bool2Str(s, d): return str(int(s)) 47 | 48 | def Str2Set(s): 49 | return set([ i for i in s.split(',') if i ]) 50 | 51 | def Set2Str(s, d): 52 | return string_literal(','.join(s), d) 53 | 54 | def Thing2Str(s, d): 55 | """Convert something into a string via str().""" 56 | return str(s) 57 | 58 | def Unicode2Str(s, d): 59 | """Convert a unicode object to a string using the default encoding. 60 | This is only used as a placeholder for the real function, which 61 | is connection-dependent.""" 62 | return s.encode() 63 | 64 | Long2Int = Thing2Str 65 | 66 | def Float2Str(o, d): 67 | return '%.15g' % o 68 | 69 | def None2NULL(o, d): 70 | """Convert None to NULL.""" 71 | return NULL # duh 72 | 73 | def Thing2Literal(o, d): 74 | 75 | """Convert something into a SQL string literal. If using 76 | MySQL-3.23 or newer, string_literal() is a method of the 77 | _mysql.MYSQL object, and this function will be overridden with 78 | that method when the connection is created.""" 79 | 80 | return string_literal(o, d) 81 | 82 | 83 | def Instance2Str(o, d): 84 | 85 | """ 86 | 87 | Convert an Instance to a string representation. If the __str__() 88 | method produces acceptable output, then you don't need to add the 89 | class to conversions; it will be handled by the default 90 | converter. If the exact class is not found in d, it will use the 91 | first class it can find for which o is an instance. 92 | 93 | """ 94 | 95 | if d.has_key(o.__class__): 96 | return d[o.__class__](o, d) 97 | cl = filter(lambda x,o=o: 98 | type(x) is types.ClassType 99 | and isinstance(o, x), d.keys()) 100 | if not cl and hasattr(types, 'ObjectType'): 101 | cl = filter(lambda x,o=o: 102 | type(x) is types.TypeType 103 | and isinstance(o, x) 104 | and d[x] is not Instance2Str, 105 | d.keys()) 106 | if not cl: 107 | return d[types.StringType](o,d) 108 | d[o.__class__] = d[cl[0]] 109 | return d[cl[0]](o, d) 110 | 111 | def char_array(s): 112 | return array.array('c', s) 113 | 114 | def array2Str(o, d): 115 | return Thing2Literal(o.tostring(), d) 116 | 117 | conversions = { 118 | types.IntType: Thing2Str, 119 | types.LongType: Long2Int, 120 | types.FloatType: Float2Str, 121 | types.NoneType: None2NULL, 122 | types.TupleType: escape_sequence, 123 | types.ListType: escape_sequence, 124 | types.DictType: escape_dict, 125 | types.InstanceType: Instance2Str, 126 | array.ArrayType: array2Str, 127 | types.StringType: Thing2Literal, # default 128 | types.UnicodeType: Unicode2Str, 129 | types.ObjectType: Instance2Str, 130 | types.BooleanType: Bool2Str, 131 | DateTimeType: DateTime2literal, 132 | DateTimeDeltaType: DateTimeDelta2literal, 133 | set: Set2Str, 134 | FIELD_TYPE.TINY: int, 135 | FIELD_TYPE.SHORT: int, 136 | FIELD_TYPE.LONG: long, 137 | FIELD_TYPE.FLOAT: float, 138 | FIELD_TYPE.DOUBLE: float, 139 | FIELD_TYPE.DECIMAL: float, 140 | FIELD_TYPE.NEWDECIMAL: float, 141 | FIELD_TYPE.LONGLONG: long, 142 | FIELD_TYPE.INT24: int, 143 | FIELD_TYPE.YEAR: int, 144 | FIELD_TYPE.SET: Str2Set, 145 | FIELD_TYPE.TIMESTAMP: mysql_timestamp_converter, 146 | FIELD_TYPE.DATETIME: DateTime_or_None, 147 | FIELD_TYPE.TIME: TimeDelta_or_None, 148 | FIELD_TYPE.DATE: Date_or_None, 149 | FIELD_TYPE.BLOB: [ 150 | (FLAG.BINARY, str), 151 | ], 152 | FIELD_TYPE.STRING: [ 153 | (FLAG.BINARY, str), 154 | ], 155 | FIELD_TYPE.VAR_STRING: [ 156 | (FLAG.BINARY, str), 157 | ], 158 | FIELD_TYPE.VARCHAR: [ 159 | (FLAG.BINARY, str), 160 | ], 161 | } 162 | 163 | try: 164 | from decimal import Decimal 165 | conversions[FIELD_TYPE.DECIMAL] = Decimal 166 | conversions[FIELD_TYPE.NEWDECIMAL] = Decimal 167 | except ImportError: 168 | pass 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/monkey.py: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (C) 2012-2013 SINA, All rights reserved. 3 | 4 | def patch(): 5 | import sys 6 | 7 | if 'MySQLdb' in sys.modules: 8 | import warnings 9 | warnings.warn('MySQLdb has alreay been imported', Warning) 10 | 11 | modules_to_replace = ( 12 | 'MySQLdb', 13 | 'MySQLdb.release', 14 | 'MySQLdb.connections', 15 | 'MySQLdb.cursors', 16 | 'MySQLdb.converters', 17 | 'MySQLdb.constants', 18 | 'MySQLdb.constants.CLIENT', 19 | 'MySQLdb.constants.FIELD_TYPE', 20 | 'MySQLdb.constants.FLAG', 21 | ) 22 | 23 | for name in modules_to_replace: 24 | if name in sys.modules: 25 | sys.modules.pop(name) 26 | 27 | import sae._restful_mysql 28 | from sae._restful_mysql import _mysql, _mysql_exceptions 29 | sys.modules['MySQLdb'] = sae._restful_mysql 30 | sys.modules['_mysql'] = _mysql 31 | sys.modules['_mysql_exceptions'] = _mysql_exceptions 32 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/release.py: -------------------------------------------------------------------------------- 1 | 2 | __author__ = "Andy Dustman " 3 | version_info = (1,2,3,'final',0) 4 | __version__ = "1.2.3" 5 | -------------------------------------------------------------------------------- /dev_server/sae/_restful_mysql/times.py: -------------------------------------------------------------------------------- 1 | """times module 2 | 3 | This module provides some Date and Time classes for dealing with MySQL data. 4 | 5 | Use Python datetime module to handle date and time columns.""" 6 | 7 | import math 8 | from time import localtime 9 | from datetime import date, datetime, time, timedelta 10 | from _mysql import string_literal 11 | 12 | Date = date 13 | Time = time 14 | TimeDelta = timedelta 15 | Timestamp = datetime 16 | 17 | DateTimeDeltaType = timedelta 18 | DateTimeType = datetime 19 | 20 | def DateFromTicks(ticks): 21 | """Convert UNIX ticks into a date instance.""" 22 | return date(*localtime(ticks)[:3]) 23 | 24 | def TimeFromTicks(ticks): 25 | """Convert UNIX ticks into a time instance.""" 26 | return time(*localtime(ticks)[3:6]) 27 | 28 | def TimestampFromTicks(ticks): 29 | """Convert UNIX ticks into a datetime instance.""" 30 | return datetime(*localtime(ticks)[:6]) 31 | 32 | format_TIME = format_DATE = str 33 | 34 | def format_TIMEDELTA(v): 35 | seconds = int(v.seconds) % 60 36 | minutes = int(v.seconds / 60) % 60 37 | hours = int(v.seconds / 3600) % 24 38 | return '%d %d:%d:%d' % (v.days, hours, minutes, seconds) 39 | 40 | def format_TIMESTAMP(d): 41 | return d.strftime("%Y-%m-%d %H:%M:%S") 42 | 43 | 44 | def DateTime_or_None(s): 45 | if ' ' in s: 46 | sep = ' ' 47 | elif 'T' in s: 48 | sep = 'T' 49 | else: 50 | return Date_or_None(s) 51 | 52 | try: 53 | d, t = s.split(sep, 1) 54 | return datetime(*[ int(x) for x in d.split('-')+t.split(':') ]) 55 | except: 56 | return Date_or_None(s) 57 | 58 | def TimeDelta_or_None(s): 59 | try: 60 | h, m, s = s.split(':') 61 | h, m, s = int(h), int(m), float(s) 62 | td = timedelta(hours=abs(h), minutes=m, seconds=int(s), 63 | microseconds=int(math.modf(s)[0] * 1000000)) 64 | if h < 0: 65 | return -td 66 | else: 67 | return td 68 | except ValueError: 69 | # unpacking or int/float conversion failed 70 | return None 71 | 72 | def Time_or_None(s): 73 | try: 74 | h, m, s = s.split(':') 75 | h, m, s = int(h), int(m), float(s) 76 | return time(hour=h, minute=m, second=int(s), 77 | microsecond=int(math.modf(s)[0] * 1000000)) 78 | except ValueError: 79 | return None 80 | 81 | def Date_or_None(s): 82 | try: return date(*[ int(x) for x in s.split('-',2)]) 83 | except: return None 84 | 85 | def DateTime2literal(d, c): 86 | """Format a DateTime object as an ISO timestamp.""" 87 | return string_literal(format_TIMESTAMP(d),c) 88 | 89 | def DateTimeDelta2literal(d, c): 90 | """Format a DateTimeDelta object as a time.""" 91 | return string_literal(format_TIMEDELTA(d),c) 92 | 93 | def mysql_timestamp_converter(s): 94 | """Convert a MySQL TIMESTAMP to a Timestamp object.""" 95 | # MySQL>4.1 returns TIMESTAMP in the same format as DATETIME 96 | if s[4] == '-': return DateTime_or_None(s) 97 | s = s + "0"*(14-len(s)) # padding 98 | parts = map(int, filter(None, (s[:4],s[4:6],s[6:8], 99 | s[8:10],s[10:12],s[12:14]))) 100 | try: return Timestamp(*parts) 101 | except: return None 102 | -------------------------------------------------------------------------------- /dev_server/sae/channel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*-coding: utf8 -*- 3 | 4 | """Channel API 5 | """ 6 | 7 | import time 8 | import json 9 | import os 10 | 11 | MAXIMUM_CLIENT_ID_LENGTH = 118 12 | 13 | MAXIMUM_TOKEN_DURATION_SECONDS = 24 * 60 14 | 15 | MAXIMUM_MESSAGE_LENGTH = 32767 16 | 17 | class Error(Exception): 18 | """Base error class for this module.""" 19 | 20 | class InvalidChannelClientIdError(Error): 21 | """Error that indicates a bad client id.""" 22 | 23 | class InvalidChannelTokenDurationError(Error): 24 | """Error that indicates the requested duration is invalid.""" 25 | 26 | class InvalidMessageError(Error): 27 | """Error that indicates a message is malformed.""" 28 | 29 | class InternalError(Error): 30 | """Error that indicates server side error""" 31 | 32 | def _validate_client_id(client_id): 33 | if not isinstance(client_id, basestring): 34 | raise InvalidChannelClientIdError('"%s" is not a string.' % client_id) 35 | 36 | if isinstance(client_id, unicode): 37 | client_id = client_id.encode('utf-8') 38 | 39 | if len(client_id) > MAXIMUM_CLIENT_ID_LENGTH: 40 | msg = 'Client id length %d is greater than max length %d' % ( 41 | len(client_id), MAXIMUM_CLIENT_ID_LENGTH) 42 | raise InvalidChannelClientIdError(msg) 43 | 44 | return client_id 45 | 46 | def create_channel(name, duration=None): 47 | client_id = _validate_client_id(name) 48 | 49 | if duration is not None: 50 | if not isinstance(duration, (int, long)): 51 | raise InvalidChannelTokenDurationError( 52 | 'Argument duration must be integral') 53 | elif duration < 1: 54 | raise InvalidChannelTokenDurationError( 55 | 'Argument duration must not be less than 1') 56 | elif duration > MAXIMUM_TOKEN_DURATION_SECONDS: 57 | msg = ('Argument duration must be less than %d' 58 | % (MAXIMUM_TOKEN_DURATION_SECONDS + 1)) 59 | raise InvalidChannelTokenDurationError(msg) 60 | 61 | _cache[name] = [] 62 | return 'http://%s/_sae/channel/%s' % (os.environ['HTTP_HOST'], name) 63 | 64 | def send_message(name, message, async=False): 65 | client_id = name 66 | 67 | if isinstance(message, unicode): 68 | message = message.encode('utf-8') 69 | elif not isinstance(message, str): 70 | raise InvalidMessageError('Message must be a string') 71 | if len(message) > MAXIMUM_MESSAGE_LENGTH: 72 | raise InvalidMessageError( 73 | 'Message must be no longer than %d chars' % MAXIMUM_MESSAGE_LENGTH) 74 | 75 | if name in _cache: 76 | _cache[name].append(message) 77 | return 1 78 | else: 79 | return 0 80 | 81 | _cache = {} 82 | 83 | import urllib 84 | import urlparse 85 | import cStringIO 86 | 87 | def _channel_wrapper(app): 88 | def _(environ, start_response): 89 | if not environ['PATH_INFO'].startswith('/_sae/channel/dev'): 90 | return app(environ, start_response) 91 | 92 | qs = urlparse.parse_qs(environ['QUERY_STRING']) 93 | 94 | token = qs['channel'][0] 95 | command = qs['command'][0] 96 | 97 | if token not in _cache: 98 | start_response('401 forbidden', []) 99 | return [] 100 | 101 | start_response('200 ok', []) 102 | 103 | if command == 'poll': 104 | try: 105 | return [_cache[token].pop(0),] 106 | except IndexError: 107 | return [] 108 | else: 109 | qs = urllib.urlencode({'from': token}) 110 | environ['PATH_INFO'] = '/_sae/channel/%sed' % command 111 | environ['QUERY_STRING'] = '' 112 | environ['REQUEST_METHOD'] = 'POST' 113 | environ['HTTP_CONTENT_TYPE'] = 'application/x-www-form-urlencoded' 114 | environ['HTTP_CONTENT_LENGTH'] = len(qs) 115 | environ['wsgi.input'] = cStringIO.StringIO(qs) 116 | try: 117 | print '[CHANNEL]', [i for i in app(environ, lambda x, y: None)] 118 | except Exception: 119 | pass 120 | return [] 121 | 122 | return _ 123 | -------------------------------------------------------------------------------- /dev_server/sae/channel.src.js: -------------------------------------------------------------------------------- 1 | // ==ClosureCompiler== 2 | // @output_file_name default.js 3 | // @compilation_level SIMPLE_OPTIMIZATIONS 4 | // @use_closure_library true 5 | // @formatting pretty_print 6 | // ==/ClosureCompiler== 7 | 8 | goog.provide('sae'); 9 | 10 | goog.require('goog.dom'); 11 | goog.require('goog.net.XhrIo'); 12 | goog.require('goog.Uri.QueryData'); 13 | 14 | appengine = {}; 15 | 16 | appengine.DevSocket = function(url) { 17 | this.readyState = appengine.DevSocket.ReadyState.CONNECTING; 18 | this.token_ = url.substring(url.lastIndexOf("/") + 1); 19 | this.applicationKey_ = this.token_; 20 | this.clientId_ = null; 21 | this.win_ = goog.dom.getWindow(); 22 | this.pollingTimer_ = null; 23 | goog.net.XhrIo.send(this.getUrl_("connect"), goog.bind(this.connect_, this)); 24 | goog.events.listen(this.win_, "beforeunload", goog.bind(this.beforeunload_, this)); 25 | if(!document.body) { 26 | throw"document.body is not defined -- do not create socket from script in ."; 27 | } 28 | }; 29 | 30 | appengine.DevSocket.POLLING_TIMEOUT_MS = 500; 31 | appengine.DevSocket.BASE_URL = "/_sae/channel/"; 32 | appengine.DevSocket.ReadyState = {CONNECTING:0, OPEN:1, CLOSING:2, CLOSED:3}; 33 | 34 | appengine.DevSocket.prototype.getUrl_ = function(command) { 35 | var url = appengine.DevSocket.BASE_URL + "dev?command=" + command + "&channel=" + this.token_; 36 | this.clientId_ && (url += "&client=" + this.clientId_); 37 | return url 38 | }; 39 | 40 | appengine.DevSocket.prototype.connect_ = function(e) { 41 | var xhr = e.target; 42 | if(xhr.isSuccess()) { 43 | this.clientId_ = xhr.getResponseText(), this.readyState = appengine.DevSocket.ReadyState.OPEN, this.onopen(), this.pollingTimer_ = this.win_.setTimeout(goog.bind(this.poll_, this), appengine.DevSocket.POLLING_TIMEOUT_MS) 44 | }else { 45 | this.readyState = appengine.DevSocket.ReadyState.CLOSING; 46 | var evt = {}; 47 | evt.description = xhr.getStatusText(); 48 | evt.code = xhr.getStatus(); 49 | this.onerror(evt); 50 | this.readyState = appengine.DevSocket.ReadyState.CLOSED; 51 | this.onclose() 52 | } 53 | }; 54 | 55 | appengine.DevSocket.prototype.disconnect_ = function() { 56 | this.readyState = appengine.DevSocket.ReadyState.CLOSED; 57 | this.onclose() 58 | }; 59 | 60 | appengine.DevSocket.prototype.forwardMessage_ = function(e) { 61 | var xhr = e.target; 62 | if(xhr.isSuccess()) { 63 | var evt = {}; 64 | evt.data = xhr.getResponseText(); 65 | if(evt.data.length) { 66 | this.onmessage(evt) 67 | } 68 | this.readyState == appengine.DevSocket.ReadyState.OPEN && (this.pollingTimer_ = this.win_.setTimeout(goog.bind(this.poll_, this), appengine.DevSocket.POLLING_TIMEOUT_MS)) 69 | }else { 70 | evt = {}, evt.description = xhr.getStatusText(), evt.code = xhr.getStatus(), this.onerror(evt), this.readyState = appengine.DevSocket.ReadyState.CLOSED, this.onclose() 71 | } 72 | }; 73 | 74 | appengine.DevSocket.prototype.poll_ = function() { 75 | goog.net.XhrIo.send(this.getUrl_("poll"), goog.bind(this.forwardMessage_, this)) 76 | }; 77 | 78 | appengine.DevSocket.prototype.beforeunload_ = function() { 79 | var xhr = goog.net.XmlHttp(); 80 | xhr.open("GET", this.getUrl_("disconnect"), !1); 81 | xhr.send() 82 | }; 83 | 84 | appengine.DevSocket.prototype.forwardSendComplete_ = function(e) { 85 | var xhr = e.target; 86 | if(!xhr.isSuccess()) { 87 | var evt = {}; 88 | evt.description = xhr.getStatusText(); 89 | evt.code = xhr.getStatus(); 90 | this.onerror(evt) 91 | } 92 | }; 93 | 94 | appengine.DevSocket.prototype.onopen = function() {}; 95 | appengine.DevSocket.prototype.onmessage = function() {}; 96 | appengine.DevSocket.prototype.onerror = function() {}; 97 | appengine.DevSocket.prototype.onclose = function() {}; 98 | 99 | appengine.DevSocket.prototype.send = function(data) { 100 | if(this.readyState != appengine.DevSocket.ReadyState.OPEN) { 101 | return!1 102 | } 103 | var url = appengine.DevSocket.BASE_URL + "message", sendData = new goog.Uri.QueryData; 104 | sendData.set("from", this.applicationKey_); 105 | sendData.set("message", data); 106 | goog.net.XhrIo.send(url, goog.bind(this.forwardSendComplete_, this), "POST", sendData.toString()); 107 | return!0 108 | }; 109 | 110 | appengine.DevSocket.prototype.close = function() { 111 | this.readyState = appengine.DevSocket.ReadyState.CLOSING; 112 | this.pollingTimer_ && this.win_.clearTimeout(this.pollingTimer_); 113 | goog.net.XhrIo.send(this.getUrl_("disconnect"), goog.bind(this.disconnect_, this)) 114 | }; 115 | 116 | goog.exportSymbol("sae.Channel.prototype.onopen", appengine.DevSocket.prototype.onopen); 117 | goog.exportSymbol("sae.Channel.prototype.onmessage", appengine.DevSocket.prototype.onmessage); 118 | goog.exportSymbol("sae.Channel.prototype.onerror", appengine.DevSocket.prototype.onerror); 119 | goog.exportSymbol("sae.Channel.prototype.onclose", appengine.DevSocket.prototype.onclose); 120 | goog.exportSymbol("sae.Channel", appengine.DevSocket); 121 | goog.exportSymbol("sae.Channel.ReadyState", appengine.DevSocket.ReadyState); 122 | goog.exportSymbol("sae.Channel.prototype.send", appengine.DevSocket.prototype.send); 123 | goog.exportSymbol("sae.Channel.prototype.close", appengine.DevSocket.prototype.close); 124 | -------------------------------------------------------------------------------- /dev_server/sae/conf.py: -------------------------------------------------------------------------------- 1 | """ SAE Settings""" 2 | 3 | import os 4 | 5 | SAE_STOREHOST = 'http://stor.sae.sina.com.cn/storageApi.php' 6 | SAE_S3HOST = 'http://s3.sae.sina.com.cn/s3Api.php' 7 | SAE_TMP_PATH = '$SAE_TMPFS_PATH' 8 | 9 | SAE_MYSQL_HOST_M = 'w.rdc.sae.sina.com.cn' 10 | SAE_MYSQL_HOST_S = 'r.rdc.sae.sina.com.cn' 11 | SAE_MYSQL_PORT = '3307' 12 | 13 | SAE_FETCHURL_HOST = 'http://fetchurl.sae.sina.com.cn' 14 | 15 | -------------------------------------------------------------------------------- /dev_server/sae/const.py: -------------------------------------------------------------------------------- 1 | """Constants about app 2 | 3 | """ 4 | import os 5 | import conf 6 | 7 | # Private 8 | APP_NAME = os.environ.get('APP_NAME', '') 9 | APP_HASH = os.environ.get('APP_HASH', '') 10 | ACCESS_KEY = os.environ.get('ACCESS_KEY', '') 11 | SECRET_KEY = os.environ.get('SECRET_KEY', '') 12 | 13 | # Public 14 | MYSQL_DB = '_'.join(['app', APP_NAME]) 15 | MYSQL_USER = ACCESS_KEY 16 | MYSQL_PASS = SECRET_KEY 17 | MYSQL_HOST = conf.SAE_MYSQL_HOST_M 18 | MYSQL_PORT = conf.SAE_MYSQL_PORT 19 | MYSQL_HOST_S = conf.SAE_MYSQL_HOST_S 20 | -------------------------------------------------------------------------------- /dev_server/sae/core.py: -------------------------------------------------------------------------------- 1 | """Core functions of SAE 2 | 3 | environ A copy of the environ passed to your wsgi app, should not be used directly 4 | 5 | """ 6 | 7 | def get_access_key(): 8 | """Return access_key of your app""" 9 | return environ.get('HTTP_ACCESSKEY', '') 10 | 11 | def get_secret_key(): 12 | """Return secret_key of your app""" 13 | return environ.get('HTTP_SECRETKEY', '') 14 | 15 | def get_trusted_hosts(): 16 | return [host for host in environ.get('TRUSTED_HOSTS', '').split() if host] 17 | 18 | environ = {} 19 | -------------------------------------------------------------------------------- /dev_server/sae/ext/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/dev_server/sae/ext/__init__.py -------------------------------------------------------------------------------- /dev_server/sae/ext/django/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/dev_server/sae/ext/django/__init__.py -------------------------------------------------------------------------------- /dev_server/sae/ext/django/mail/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/dev_server/sae/ext/django/mail/__init__.py -------------------------------------------------------------------------------- /dev_server/sae/ext/django/mail/backend.py: -------------------------------------------------------------------------------- 1 | """send mail via sae's mail service""" 2 | 3 | import threading 4 | 5 | from django.conf import settings 6 | from django.core.mail.backends.base import BaseEmailBackend 7 | 8 | from email.mime.base import MIMEBase 9 | 10 | from sae.mail import EmailMessage, Error 11 | 12 | class EmailBackend(BaseEmailBackend): 13 | def __init__(self, host=None, port=None, username=None, password=None, 14 | use_tls=None, fail_silently=False, **kwargs): 15 | super(EmailBackend, self).__init__(fail_silently=fail_silently) 16 | self.host = host or settings.EMAIL_HOST 17 | self.port = port or settings.EMAIL_PORT 18 | if username is None: 19 | self.username = settings.EMAIL_HOST_USER 20 | else: 21 | self.username = username 22 | if password is None: 23 | self.password = settings.EMAIL_HOST_PASSWORD 24 | else: 25 | self.password = password 26 | if use_tls is None: 27 | self.use_tls = settings.EMAIL_USE_TLS 28 | else: 29 | self.use_tls = use_tls 30 | self.smtp = (self.host, self.port, self.username, self.password, 31 | self.use_tls) 32 | self._lock = threading.RLock() 33 | 34 | def send_messages(self, email_messages): 35 | if not email_messages: 36 | return 37 | with self._lock: 38 | num_sent = 0 39 | for message in email_messages: 40 | sent = self._send(message) 41 | if sent: 42 | num_sent += 1 43 | return num_sent 44 | 45 | def _send(self, email_message): 46 | if not email_message.recipients(): 47 | return False 48 | attachments = [] 49 | for attach in email_message.attachments: 50 | if isinstance(attach, MIMEBase): 51 | if not self.fail_silently: 52 | raise NotImplemented() 53 | else: 54 | return False 55 | else: 56 | attachments.append((attach[0], attach[1])) 57 | try: 58 | message = EmailMessage() 59 | message.to = email_message.recipients() 60 | message.from_addr = email_message.from_email 61 | message.subject = email_message.subject 62 | message.body = email_message.body 63 | message.smtp = self.smtp 64 | if attachments: 65 | message.attachments = attachments 66 | message.send() 67 | except Error, e: 68 | if not self.fail_silently: 69 | raise 70 | return False 71 | return True 72 | -------------------------------------------------------------------------------- /dev_server/sae/ext/django/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/dev_server/sae/ext/django/storage/__init__.py -------------------------------------------------------------------------------- /dev_server/sae/ext/django/storage/backend.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from StringIO import StringIO 3 | 4 | from django.conf import settings 5 | from django.core.files.base import File 6 | from django.core.files.storage import Storage 7 | from django.core.exceptions import ImproperlyConfigured 8 | 9 | from sae.storage import Connection, Error 10 | 11 | from sae.const import ACCESS_KEY, SECRET_KEY, APP_NAME 12 | 13 | STORAGE_BUCKET_NAME = getattr(settings, 'STORAGE_BUCKET_NAME') 14 | STORAGE_ACCOUNT = getattr(settings, 'STORAGE_ACCOUNT', APP_NAME) 15 | STORAGE_ACCESSKEY = getattr(settings, 'STORAGE_ACCESSKEY', ACCESS_KEY) 16 | STORAGE_SECRETKEY = getattr(settings, 'STORAGE_SECRETKEY', SECRET_KEY) 17 | STORAGE_GZIP = getattr(settings, 'STORAGE_GZIP', False) 18 | 19 | class Storage(Storage): 20 | def __init__(self, bucket_name=STORAGE_BUCKET_NAME, 21 | accesskey=STORAGE_ACCESSKEY, secretkey=STORAGE_SECRETKEY, 22 | account=STORAGE_ACCOUNT): 23 | conn = Connection(accesskey, secretkey, account) 24 | self.bucket = conn.get_bucket(bucket_name) 25 | 26 | def _open(self, name, mode='rb'): 27 | name = self._normalize_name(name) 28 | return StorageFile(name, mode, self) 29 | 30 | def _save(self, name, content): 31 | name = self._normalize_name(name) 32 | try: 33 | self.bucket.put_object(name, content) 34 | except Error, e: 35 | raise IOError('Storage Error: %s' % e.args) 36 | return name 37 | 38 | def delete(self, name): 39 | name = self._normalize_name(name) 40 | try: 41 | self.bucket.delete_object(name) 42 | except Error, e: 43 | raise IOError('Storage Error: %s' % e.args) 44 | 45 | def exists(self, name): 46 | name = self._normalize_name(name) 47 | try: 48 | self.bucket.stat_object(name) 49 | except Error, e: 50 | if e[0] == 404: 51 | return False 52 | raise 53 | return True 54 | 55 | #def listdir(self, path): 56 | # path = self._normalize_name(path) 57 | # try: 58 | # result = self.bucket.list(path=path) 59 | # return [i.name for i in result] 60 | # except Error, e: 61 | # raise IOError('Storage Error: %s' % e.args) 62 | 63 | def size(self, name): 64 | name = self._normalize_name(name) 65 | try: 66 | attrs = self.bucket.stat_object(name) 67 | return attrs.bytes 68 | except Error, e: 69 | raise IOError('Storage Error: %s' % e.args) 70 | 71 | def url(self, name): 72 | name = self._normalize_name(name) 73 | return self.bucket.generate_url(name) 74 | 75 | def _open_read(self, name): 76 | name = self._normalize_name(name) 77 | class _: 78 | def __init__(self, chunks): 79 | self.buf = '' 80 | def read(self, num_bytes=None): 81 | if num_bytes is None: 82 | num_bytes = sys.maxint 83 | try: 84 | while len(self.buf) < num_bytes: 85 | self.buf += chunks.next() 86 | except StopIteration: 87 | pass 88 | except Error, e: 89 | raise IOError('Storage Error: %s' % e.args) 90 | retval = self.buf[:num_bytes] 91 | self.buf = self.buf[num_bytes:] 92 | return retval 93 | chunks = self.bucket.get_object_contents(name, chunk_size=8192) 94 | return _(chunks) 95 | 96 | def _normalize_name(self, name): 97 | return name.lstrip('/') 98 | 99 | class StorageFile(File): 100 | def __init__(self, name, mode, storage): 101 | self.name = name 102 | self.mode = mode 103 | self.file = StringIO() 104 | self._storage = storage 105 | self._is_dirty = False 106 | 107 | @property 108 | def size(self): 109 | if hasattr(self, '_size'): 110 | self._size = self.storage.size() 111 | return self._size 112 | 113 | def read(self, num_bytes=None): 114 | if not hasattr(self, '_obj'): 115 | self._obj = self._storage._open_read(self.name) 116 | return self._obj.read(num_bytes) 117 | 118 | def write(self, content): 119 | if 'w' not in self._mode: 120 | raise AttributeError("File was opened for read-only access.") 121 | self.file = StringIO(content) 122 | self._is_dirty = True 123 | 124 | def close(self): 125 | if self._is_dirty: 126 | self._storage._save(self.name, self.file.getvalue()) 127 | self.file.close() 128 | -------------------------------------------------------------------------------- /dev_server/sae/ext/shell.py: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (C) 2012-2013 SINA, All rights reserved. 3 | 4 | ShellMiddleware = lambda x: x 5 | -------------------------------------------------------------------------------- /dev_server/sae/ext/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/dev_server/sae/ext/storage/__init__.py -------------------------------------------------------------------------------- /dev_server/sae/ext/storage/monkey.py: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (C) 2012-2013 SINA, All rights reserved. 3 | 4 | import os.path 5 | import sys 6 | import re 7 | import time 8 | import errno 9 | 10 | from sae.storage import Connection, Error 11 | 12 | _S_FILEPATH_REGEX = re.compile('^(?:/s|/s/.*)$') 13 | _S_FILENAME_REGEX = re.compile('^(?:/s|/s/([^/]*)/?(.*))$') 14 | 15 | def _parse_name(filename): 16 | m = _S_FILENAME_REGEX.match(os.path.normpath(filename)) 17 | if m: 18 | return m.groups() 19 | else: 20 | raise ValueError('invalid filename') 21 | 22 | STORAGE_PATH = os.environ.get('sae.storage.path') 23 | 24 | _is_storage_path = lambda n: _S_FILEPATH_REGEX.match(n) 25 | def _get_storage_path(path): 26 | if not STORAGE_PATH: 27 | raise RuntimeError( 28 | "Please specify --storage-path in the command line") 29 | return STORAGE_PATH + n[2:] 30 | 31 | class _File(file): 32 | 33 | def isatty(self): 34 | return False 35 | 36 | # Unimplemented interfaces below here. 37 | 38 | def flush(self): 39 | pass 40 | 41 | def fileno(self): 42 | raise NotImplementedError() 43 | 44 | def next(self): 45 | raise NotImplementedError() 46 | 47 | def readinto(self): 48 | raise NotImplementedError() 49 | 50 | def readline(self): 51 | raise NotImplementedError() 52 | 53 | def readlines(self): 54 | raise NotImplementedError() 55 | 56 | def truncate(self): 57 | raise NotImplementedError() 58 | 59 | def writelines(self): 60 | raise NotImplementedError() 61 | 62 | def xreadlines(self): 63 | raise NotImplementedError() 64 | 65 | import __builtin__ 66 | 67 | _real_open = __builtin__.open 68 | def open(filename, mode='r', buffering=-1): 69 | if _is_storage_path(filename): 70 | filename = _get_storage_path(filename) 71 | return _real_open(filename, mode, buffering) 72 | 73 | import os 74 | 75 | _real_os_listdir = os.listdir 76 | def os_listdir(path): 77 | if _is_storage_path(path): 78 | path = _get_storage_path(path) 79 | return _real_os_listdir(path) 80 | 81 | _real_os_mkdir = os.mkdir 82 | def os_mkdir(path, mode=0777): 83 | if _is_storage_path(path): 84 | path = _get_storage_path(path) 85 | return _real_os_mkdir(path, mode) 86 | 87 | _real_os_open = os.open 88 | def os_open(filename, flag, mode=0777): 89 | if _is_storage_path(filename): 90 | filename = _get_storage_path(filename) 91 | return _real_os_open(filename, flag, mode) 92 | 93 | _real_os_fdopen = getattr(os, 'fdopen', None) 94 | def os_fdopen(fd, mode='r', bufsize=-1): 95 | return _real_os_fdopen(fd, mode, bufsize) 96 | 97 | _real_os_close = os.close 98 | def os_close(fd): 99 | return _real_os_close(fd) 100 | 101 | _real_os_chmod = os.chmod 102 | def os_chmod(path, mode): 103 | if _is_storage_path(path): 104 | pass 105 | else: 106 | return _real_os_chmod(path, mode) 107 | 108 | _real_os_stat = os.stat 109 | def os_stat(path): 110 | if _is_storage_path(path): 111 | path = _get_storage_path(path) 112 | return _real_os_stat(path) 113 | 114 | _real_os_unlink = os.unlink 115 | def os_unlink(path): 116 | if _is_storage_path(path): 117 | path = _get_storage_path(path) 118 | return _real_os_unlink(path) 119 | 120 | import os.path 121 | 122 | _real_os_path_exists = os.path.exists 123 | def os_path_exists(path): 124 | if _is_storage_path(path): 125 | path = _get_storage_path(path) 126 | return _real_os_path_exists(path) 127 | 128 | _real_os_path_isdir = os.path.isdir 129 | def os_path_isdir(path): 130 | if _is_storage_path(path): 131 | path = _get_storage_path(path) 132 | return _real_os_path_isdir(path) 133 | 134 | _real_os_rmdir = os.rmdir 135 | def os_rmdir(path): 136 | if _is_storage_path(path): 137 | path = _get_storage_path(path) 138 | return _real_os_rmdir(path) 139 | 140 | def patch_all(): 141 | __builtin__.open = open 142 | os.listdir = os_listdir 143 | os.mkdir = os_mkdir 144 | os.path.exists = os_path_exists 145 | os.path.isdir = os_path_isdir 146 | os.open = os_open 147 | os.fdopen = os_fdopen 148 | os.close = os_close 149 | os.chmod = os_chmod 150 | os.stat = os_stat 151 | os.unlink = os_unlink 152 | os.rmdir = os_rmdir 153 | -------------------------------------------------------------------------------- /dev_server/sae/sae_signature.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import base64 4 | import hmac 5 | import hashlib 6 | 7 | def get_signature(key, msg): 8 | h = hmac.new(key, msg, hashlib.sha256) 9 | return base64.b64encode(h.digest()) 10 | 11 | def get_signatured_headers(headers): 12 | """Given a list of headers, return a signatured dict 13 | Becareful of the order of headers when signaturing 14 | """ 15 | d = {} 16 | msg = '' 17 | for k, v in headers: 18 | d[k] = v 19 | msg += k + v 20 | 21 | secret = os.environ.get('SECRET_KEY', '') 22 | d['Signature'] = get_signature(secret, msg) 23 | return d 24 | -------------------------------------------------------------------------------- /dev_server/sae/util.py: -------------------------------------------------------------------------------- 1 | 2 | from sae_signature import get_signature, get_signatured_headers 3 | 4 | def half_secret(d, k): 5 | """Hidden part of the secret""" 6 | l = len(d[k]) 7 | if l > 2: 8 | d[k] = d[k][:2] + '*' * (l - 2) 9 | else: 10 | d[k] = '*' * l 11 | 12 | def protect_secret(d): 13 | for k, v in d.items(): 14 | if 'KEY' in k: 15 | half_secret(d, k) 16 | 17 | import os.path 18 | 19 | def search_file_bottom_up(name): 20 | curdir = os.getcwd() 21 | 22 | while True: 23 | path = os.path.join(curdir, name) 24 | if os.path.isfile(path): 25 | return curdir 26 | _curdir = os.path.dirname(curdir) 27 | if _curdir == curdir: 28 | return None 29 | curdir = _curdir 30 | 31 | -------------------------------------------------------------------------------- /dev_server/setup.py: -------------------------------------------------------------------------------- 1 | 2 | import os.path 3 | from setuptools import setup, find_packages 4 | 5 | VERSION = '1.3.6' 6 | 7 | scripts = ['dev_server.py', 'saecloud', 'cloudsql.py'] 8 | 9 | if os.name == 'nt': 10 | # XXX: shebang does not work on windows 11 | BAT = 'saecloud.bat' 12 | f = os.path.join(os.path.dirname(__file__), BAT) 13 | open(f, 'w').write('@python "%~dp0\saecloud" %*') 14 | scripts.append(BAT) 15 | 16 | setup( 17 | name = 'sae-python-dev', 18 | version = VERSION, 19 | author = 'SAE Python Team', 20 | author_email = 'saemail@sina.cn', 21 | description = ('SAE Python development server'), 22 | install_requires = [ 23 | 'Werkzeug', 24 | 'pip', 25 | 'PyYAML', 26 | 'argparse', 27 | # XXX: The latest grizzled-python package is broken 28 | 'grizzled-python==1.0.1', 29 | 'sqlcmd', 30 | 'prettytable', 31 | ], 32 | platforms='any', 33 | url = "http://python.sinaapp.com", 34 | packages=find_packages(), 35 | package_data={'sae': ['channel.js']}, 36 | scripts = scripts, 37 | zip_safe = False, 38 | ) 39 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SAEPythonReferences.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SAEPythonReferences.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/SAEPythonReferences" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SAEPythonReferences" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /docs/exts/chinese_search.py: -------------------------------------------------------------------------------- 1 | from sphinx.search import SearchLanguage 2 | import jieba 3 | 4 | class SearchChinese(SearchLanguage): 5 | lang = 'zh' 6 | 7 | def init(self, options): 8 | pass 9 | 10 | def split(self, input): 11 | return jieba.cut_for_search(input.encode("utf8")) 12 | 13 | def word_filter(self, stemmed_word): 14 | return True 15 | 16 | def setup(app): 17 | import sphinx.search as search 18 | search.languages["zh_CN"] = SearchChinese 19 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. SAE Python References documentation master file, created by 2 | sphinx-quickstart on Fri Sep 23 11:25:19 2011. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | SAE Python 开发者手册 7 | ================================================= 8 | 9 | 本文档,示例代码以及本地测试server下载: :: 10 | 11 | git clone https://github.com/sinacloud/sae-python-dev-guide.git 12 | 13 | Contents: 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | 18 | quickstart 19 | runtime 20 | service 21 | tools 22 | faq 23 | 24 | 25 | Indices and tables 26 | ================== 27 | 28 | * :ref:`genindex` 29 | * :ref:`modindex` 30 | * :ref:`search` 31 | 32 | -------------------------------------------------------------------------------- /docs/runtime.rst: -------------------------------------------------------------------------------- 1 | SAE Python环境 2 | ======================= 3 | 4 | SAE应用运行于沙箱环境之中,SAE会根据负载在后端的多个节点中选择一个来处理HTTP请求。 5 | SAE Python支持标准WSGI应用。 6 | 7 | 请求处理 8 | ------------- 9 | 10 | SAE使用请求的域名来决定处理的应用。比如 `http://your_app_name.sinaapp.com` 的请求会被路由给名为 11 | `your_app_name` 的应用来处理。 12 | 13 | 每个应用可以同时运行多个版本,版本以数字为标示,默认版本为1。如果需要访问非默认版本, 14 | 需要在域名的前面加上版本号作为子域名: `http://version.your_app_name.sinaapp.com` 。 15 | 16 | 默认URL符合以下规则的请求会作为静态文件处理: 17 | 18 | * /static/\* 19 | * /favicon.ico 20 | 21 | 其他所有请求,都被路由到/index.wsgi:application,即应用根目录index.wsgi文件, 22 | 名为application的callable,不可修改。 23 | 24 | application 使用下列方式创建 25 | 26 | .. module:: sae 27 | .. function:: create_wsgi_app(app) 28 | 29 | 将标准wsgi应用封装为适宜在SAE上运行的应用 30 | 31 | :: 32 | 33 | import sae 34 | 35 | def app(environ, start_response): 36 | # Your app 37 | ... 38 | 39 | application = sae.create_wsgi_app(app) 40 | 41 | 所有请求的最大执行时间为 `300s` ,超过该时间的请求会被系统强制结束,可能会导致返回502。 42 | 43 | 对于每个应用的请求,系统会为其维护一个队列,如果出现请求在一定的时间里得不到处理, 44 | 服务器会自动给该应用增加instance。如果某一个instance在一段时间里没有任何请求,会被系统回收。 45 | 46 | Python环境 47 | ------------------- 48 | 49 | Python runtime使用的是Python 2.7.3。 50 | 51 | * 仅支持运行纯Python的应用,不能动态加载C扩展,即.so,.dll等格式的模块不能使用 52 | * 进程,线程操作受限 53 | * 本地文件系统只读。应用可以读取本应用目录,Python标准库下的内容,如需读写临时文件建议使用StringIO或者cStringIO来替代。 54 | 55 | Python默认的模块搜索路径为:当前目录 > 系统目录。添加模块搜索目录的方法为: :: 56 | 57 | import sys 58 | sys.path.insert(0, your_custom_module_path) 59 | 60 | 注意:Python当前目录下的子目录只有包含__init__.py才会被Python认为是一个package, 61 | 才可以直接import。 62 | 63 | 用户可以上传和使用 `.pyc` 文件,注意 `.pyc` 文件必须是python2.7.3生成的,否则无效。 64 | 65 | SAE设置了一些自定义的环境变量,这些环境变量可以通过os.environ这个dict获取。 66 | 67 | + APP_NAME:应用名。 68 | + APP_VERSION: 当前应用使用的版本号。 69 | + SERVER_SOFTWARE: 当前server的版本(目前为direwolf/0.1)。 70 | 可以使用这个环境变量来区分本地开发环境还是在线环境,本地开发环境未设置这个值。 71 | 72 | 日志系统 73 | --------- 74 | 打印到stdout和stderr的内容会记录到应用的日志中心中, 75 | 所以直接使用print语句或者logging模块来记录应用的日志就可以了。 76 | 77 | 日志内容在 `应用»日志中心» HTTP` 中查看,类别为debug。 78 | 79 | .. note:: logging默认设置的level是WARNING,也就是level >= WARNING的消息才会被输出。 80 | 81 | 应用缓存 82 | ---------- 83 | 84 | SAE Python会对应用导入的模块(包括index.wsgi)进行缓存,从而缩短请求响应时间, 85 | 对于缓存了的应用,请求处理只是取出index.wsgi中application这个callable并调用。 86 | 87 | 88 | 应用程序配置 89 | ------------- 90 | 91 | 应用程序的配置文件为应用目录下的config.yaml文件。 92 | 93 | * 设置使用的worker类型 :: 94 | 95 | worker: tornado | gevent | wsgi 96 | 97 | 如果没有设置则默认使用wsgi worker。 98 | 99 | .. warning:: gevent和tornado worker还处在bleeding edge, use at your own risk。 100 | 101 | * 使用预装模块 102 | 103 | 部分预装的第三方模块不在python的默认模块搜索路径中,需要在config.yaml中配置后才可以使用。 :: 104 | 105 | libraries: 106 | - name: django 107 | version: "1.4" 108 | 109 | - name: numpy 110 | version: "1.6.1" 111 | 112 | name为第三方模块的名称,version为需要使用的版本,这两个字段为必填字段。 :ref:`pre-installed-package-list` 113 | 114 | * 静态文件处理 115 | 116 | ``静态文件夹`` :: 117 | 118 | handlers: 119 | - url: /static/ 120 | static_path: static 121 | 122 | url为URL的前缀,static_path为静态文件所在的目录(相对于应用目录)。 123 | 124 | 当请求的url为目录时,服务器会首先尝试static_path下的index.html,当index.html存在时, 125 | 返回index.html的内容,否则返回404。 126 | 127 | ``静态文件`` :: 128 | 129 | handlers: 130 | - url: /robots.txt 131 | static_path: robots.txt 132 | 133 | - url: /favicon.ico 134 | static_path: favicon.ico 135 | 136 | * gzip压缩 :: 137 | 138 | handlers: 139 | - url: /static/ 140 | gzip: on 141 | 142 | - url: /a-big-file.txt 143 | gzip: on 144 | 145 | url为URL的前缀。 146 | 147 | 注意,当客户端支持gzip(HTTP请求的Accept-Encoding中包含gzip)时,服务端才会开启压缩。 148 | 149 | .. note:: 150 | 151 | 1. 部分第三方库已经包含在默认搜索路径中,可以不在config.yaml中指定直接使用。 152 | 153 | 2. 如果config.yaml中没有设置静态文件相关的handlers,系统会默认将/static为前缀 154 | 的URL转发到应用目录下的static目录。 155 | 156 | 3. 以上两条规则仅为兼容性考虑保留,不推荐使用,请在config.yaml明确配置。 157 | 158 | 4. 对于php runtime支持但是python runtime不支持的appconfig,请考虑使用wsgi middleware来完成,参见 :ref:`wsgi_middleware` 159 | 160 | .. _pre-installed-package-list: 161 | 162 | 预装模块列表 163 | --------------------- 164 | 165 | =============================== =================== ==================== 166 | 名称 支持的版本 默认版本 167 | =============================== =================== ==================== 168 | django 1.2.7, 1.4, 1.5 1.2.7 169 | flask 0.7.2 0.7.2 170 | flask-sqlalchemy 0.15 0.15 171 | werkzeug 0.7.1 0.7.1 172 | jinja2 2.6 2.6 173 | tornado 2.1.1, 2.4.1, 3.1.1 2.1.1 174 | bottle 0.9.6 0.9.6 175 | sqlalchemy 0.7.10 0.7.10 176 | webpy 0.36 0.36 177 | PIL 1.1.7 1.1.7 178 | MySQLdb 1.2.3 1.2.3 179 | numpy 1.6.1 无 180 | lxml 2.3.4 无 181 | PyYAML 3.10 3.10 182 | misaka 1.0.2 无 183 | matplotlib 1.1.1 无 184 | PyCrypto 2.6 无 185 | py-bcrypt 0.2 无 186 | greenlet 0.4.0 0.4.0 187 | gevent 1.0rc2 1.0rc2 188 | markupsafe 0.15 无 189 | bitarray 0.8.0 无 190 | =============================== =================== ==================== 191 | 192 | .. note:: 如果模块不存在默认版本或者需要使用非默认版本请在应用配置文件config.yaml中指定。 193 | 194 | 195 | -------------------------------------------------------------------------------- /docs/static/channel-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/docs/static/channel-overview.png -------------------------------------------------------------------------------- /docs/theme/nature/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | {% set script_files = script_files + ["_static/sae.js"] %} 3 | 4 | -------------------------------------------------------------------------------- /docs/theme/nature/static/comment_white_yellow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/docs/theme/nature/static/comment_white_yellow.gif -------------------------------------------------------------------------------- /docs/theme/nature/static/nature.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * nature.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- nature theme. 6 | * 7 | * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: Arial, sans-serif; 18 | font-size: 100%; 19 | background-color: #111; 20 | color: #555; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 0 0 0 {{ theme_sidebarwidth|toint }}px; 32 | } 33 | 34 | hr { 35 | border: 1px solid #B1B4B6; 36 | } 37 | 38 | div.document { 39 | background-color: #eee; 40 | } 41 | 42 | div.body { 43 | background-color: #ffffff; 44 | color: #3E4349; 45 | padding: 0 30px 30px 30px; 46 | font-size: 0.9em; 47 | } 48 | 49 | div.footer { 50 | color: #555; 51 | width: 100%; 52 | padding: 13px 0; 53 | text-align: center; 54 | font-size: 75%; 55 | } 56 | 57 | div.footer a { 58 | color: #444; 59 | text-decoration: underline; 60 | } 61 | 62 | div.related { 63 | background-color: #6BA81E; 64 | line-height: 32px; 65 | color: #fff; 66 | text-shadow: 0px 1px 0 #444; 67 | font-size: 0.9em; 68 | } 69 | 70 | div.related a { 71 | color: #E2F3CC; 72 | } 73 | 74 | div.sphinxsidebar { 75 | font-size: 0.75em; 76 | line-height: 1.5em; 77 | } 78 | 79 | div.sphinxsidebarwrapper{ 80 | padding: 20px 0; 81 | } 82 | 83 | div.sphinxsidebar h3, 84 | div.sphinxsidebar h4 { 85 | font-family: Arial, sans-serif; 86 | color: #222; 87 | font-size: 1.2em; 88 | font-weight: normal; 89 | margin: 0; 90 | padding: 5px 10px; 91 | background-color: #ddd; 92 | text-shadow: 1px 1px 0 white 93 | } 94 | 95 | div.sphinxsidebar h4{ 96 | font-size: 1.1em; 97 | } 98 | 99 | div.sphinxsidebar h3 a { 100 | color: #444; 101 | } 102 | 103 | 104 | div.sphinxsidebar p { 105 | color: #888; 106 | padding: 5px 20px; 107 | } 108 | 109 | div.sphinxsidebar p.topless { 110 | } 111 | 112 | div.sphinxsidebar ul { 113 | margin: 10px 20px; 114 | padding: 0; 115 | color: #000; 116 | } 117 | 118 | div.sphinxsidebar a { 119 | color: #444; 120 | } 121 | 122 | div.sphinxsidebar input { 123 | border: 1px solid #ccc; 124 | font-family: sans-serif; 125 | font-size: 1em; 126 | } 127 | 128 | div.sphinxsidebar input[type=text]{ 129 | margin-left: 20px; 130 | } 131 | 132 | /* -- body styles ----------------------------------------------------------- */ 133 | 134 | a { 135 | color: #005B81; 136 | text-decoration: none; 137 | } 138 | 139 | a:hover { 140 | color: #E32E00; 141 | text-decoration: underline; 142 | } 143 | 144 | div.body h1, 145 | div.body h2, 146 | div.body h3, 147 | div.body h4, 148 | div.body h5, 149 | div.body h6 { 150 | font-family: Arial, sans-serif; 151 | background-color: #BED4EB; 152 | font-weight: normal; 153 | color: #212224; 154 | margin: 30px 0px 10px 0px; 155 | padding: 5px 0 5px 10px; 156 | text-shadow: 0px 1px 0 white 157 | } 158 | 159 | div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } 160 | div.body h2 { font-size: 150%; background-color: #C8D5E3; } 161 | div.body h3 { font-size: 120%; background-color: #D8DEE3; } 162 | div.body h4 { font-size: 110%; background-color: #D8DEE3; } 163 | div.body h5 { font-size: 100%; background-color: #D8DEE3; } 164 | div.body h6 { font-size: 100%; background-color: #D8DEE3; } 165 | 166 | a.headerlink { 167 | color: #c60f0f; 168 | font-size: 0.8em; 169 | padding: 0 4px 0 4px; 170 | text-decoration: none; 171 | } 172 | 173 | a.headerlink:hover { 174 | background-color: #c60f0f; 175 | color: white; 176 | } 177 | 178 | div.body p, div.body dd, div.body li { 179 | line-height: 1.5em; 180 | } 181 | 182 | div.admonition p.admonition-title + p { 183 | display: inline; 184 | } 185 | 186 | div.highlight{ 187 | background-color: white; 188 | } 189 | 190 | div.note { 191 | background-color: #eee; 192 | border: 1px solid #ccc; 193 | } 194 | 195 | div.seealso { 196 | background-color: #ffc; 197 | border: 1px solid #ff6; 198 | } 199 | 200 | div.topic { 201 | background-color: #eee; 202 | } 203 | 204 | div.warning { 205 | background-color: #ffe4e4; 206 | border: 1px solid #f66; 207 | } 208 | 209 | p.admonition-title { 210 | display: inline; 211 | } 212 | 213 | p.admonition-title:after { 214 | content: ":"; 215 | } 216 | 217 | pre { 218 | padding: 10px; 219 | background-color: White; 220 | color: #222; 221 | line-height: 1.2em; 222 | border: 1px solid #C6C9CB; 223 | font-size: 1.1em; 224 | margin: 1.5em 0 1.5em 0; 225 | -webkit-box-shadow: 1px 1px 1px #d8d8d8; 226 | -moz-box-shadow: 1px 1px 1px #d8d8d8; 227 | } 228 | 229 | tt { 230 | background-color: #ecf0f3; 231 | color: #222; 232 | /* padding: 1px 2px; */ 233 | font-size: 1.1em; 234 | font-family: monospace; 235 | } 236 | 237 | .viewcode-back { 238 | font-family: Arial, sans-serif; 239 | } 240 | 241 | div.viewcode-block:target { 242 | background-color: #f4debf; 243 | border-top: 1px solid #ac9; 244 | border-bottom: 1px solid #ac9; 245 | } 246 | 247 | 248 | div.comment { 249 | position:absolute; 250 | left: -9999px; 251 | top: -9999px; 252 | } 253 | div.comment a.email_link { 254 | display: block; 255 | width: 16px; 256 | height: 16px; 257 | cursor: pointer; 258 | color: white; 259 | text-decoration:none; 260 | background: transparent url(./comment_white_yellow.gif) no-repeat -16px 0; 261 | } 262 | 263 | div.comment a.email_link:hover { 264 | background-position:0 0; 265 | } 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /docs/theme/nature/static/sae.js: -------------------------------------------------------------------------------- 1 | DESELEMENT = "h2,h3,h4"; //"h1,h2,h3,h4,ul,div.section p,div.highlight-python"; 2 | 3 | function strip_tags(st) { 4 | return st.replace(/<[^>]+>?[^<]*>/g, ''); 5 | } 6 | 7 | $(document).ready(function() { 8 | $("div.body > div.section").find(DESELEMENT).each(function() { 9 | if (!$(this).prev("div.comment").length) { 10 | var cmt = $('
'); 11 | $(this).before(cmt); 12 | cmt.offset({ 13 | //left: $(this).parents('.section').offset().left - 10, 14 | left: $(this).offset().left - 10, 15 | top: $(this).offset().top - 5 16 | }); 17 | } 18 | }); 19 | 20 | $("a.email_link").hover(function() { 21 | if ($(this).attr("href") == null||$(this).attr("href") == '') { 22 | var body = $(this).parent("div.comment").next().html(); 23 | 24 | body = strip_tags(body).replace(/¶/g, ''); 25 | if (body.length > 100) { 26 | body = body.substring(0, 100)+"..."; 27 | } 28 | 29 | $(this).attr("href" 30 | , "https://github.com/SAEPython/saepythondevguide/issues/new?title={文档}" + encodeURIComponent(body)); 31 | $(this).attr("target", "_blank"); 32 | } 33 | }, function(){ 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /docs/theme/nature/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = nature.css 4 | pygments_style = tango 5 | -------------------------------------------------------------------------------- /examples/apibus/apibus_handler.py: -------------------------------------------------------------------------------- 1 | #-*-coding: utf8 -*- 2 | 3 | """ 4 | SAE API auth handler for urllib2 and requests 5 | 6 | urllib2: 7 | 8 | >>> import urllib2 9 | >>> apibus_handler = SaeApibusAuthHandler(ACCESSKEY, SECRETKEY) 10 | >>> opener = urllib2.build_opener(apibus_handler) 11 | >>> print opener.open('http://g.sae.sina.com.cn/log/http/2015-06-18/1-access.log').read() 12 | 13 | requests: 14 | 15 | >>> import requests 16 | >>> print requests.get('http://g.sae.sina.com.cn/log/http/2015-06-18/1-access.log?head/0/10|fields/ /1/2/3/4', auth=SaeApibusAuth(ACCESSKEY, SECRETKEY)).content 17 | """ 18 | 19 | import hmac 20 | import base64 21 | import hashlib 22 | import time 23 | import urllib 24 | from urllib2 import BaseHandler, Request 25 | 26 | _APIBUS_URL_PREFIX = 'http://g.sae.sina.com.cn/' 27 | 28 | class SaeApibusAuthHandler(BaseHandler): 29 | # apibus handler must be in front 30 | handler_order = 100 31 | 32 | def __init__(self, accesskey, secretkey): 33 | self.accesskey = accesskey 34 | self.secretkey = secretkey 35 | 36 | def http_request(self, req): 37 | orig_url = req.get_full_url() 38 | if not orig_url.startswith(_APIBUS_URL_PREFIX): 39 | return req 40 | 41 | timestamp = str(int(time.time())) 42 | headers = [ 43 | ('x-sae-timestamp', timestamp), 44 | ('x-sae-accesskey', self.accesskey), 45 | ] 46 | req.headers.update(headers) 47 | 48 | method = req.get_method() 49 | resource = urllib.unquote(req.get_full_url()[len(_APIBUS_URL_PREFIX)-1:]) 50 | sae_headers = [(k.lower(), v.lower()) for k, v in req.headers.items() if k.lower().startswith('x-sae-')] 51 | req.add_header('Authorization', _signature(self.secretkey, method, resource, sae_headers)) 52 | return req 53 | 54 | https_request = http_request 55 | 56 | try: 57 | from requests.auth import AuthBase 58 | 59 | class SaeApibusAuth(AuthBase): 60 | """Attaches HTTP Basic Authentication to the given Request object.""" 61 | def __init__(self, accesskey, secretkey): 62 | self.accesskey = accesskey 63 | self.secretkey = secretkey 64 | 65 | def __call__(self, r): 66 | timestamp = str(int(time.time())) 67 | r.headers['x-sae-timestamp'] = timestamp 68 | r.headers['x-sae-accesskey'] = self.accesskey 69 | resource = urllib.unquote(r.url[len(_APIBUS_URL_PREFIX)-1:]) 70 | #resource = r.url[len(_APIBUS_URL_PREFIX)-1:] 71 | sae_headers = [(k.lower(), v.lower()) for k, v in r.headers.items() if k.lower().startswith('x-sae-')] 72 | r.headers['Authorization'] = _signature(self.secretkey, r.method, resource, sae_headers) 73 | return r 74 | except ImportError: 75 | # requests was not present! 76 | pass 77 | 78 | def _signature(secret, method, resource, headers): 79 | msgToSign = "\n".join([ 80 | method, resource, 81 | "\n".join([(k + ":" + v) for k, v in sorted(headers)]), 82 | ]) 83 | return "SAEV1_HMAC_SHA256 " + base64.b64encode(hmac.new(secret, msgToSign, hashlib.sha256).digest()) 84 | 85 | -------------------------------------------------------------------------------- /examples/bottle/README: -------------------------------------------------------------------------------- 1 | Hello, Bottle! 2 | -------------------------------------------------------------------------------- /examples/bottle/index.wsgi: -------------------------------------------------------------------------------- 1 | from bottle import Bottle, run 2 | 3 | import sae 4 | 5 | app = Bottle() 6 | 7 | @app.route('/') 8 | def hello(): 9 | return "Hello, world! - Bottle" 10 | 11 | application = sae.create_wsgi_app(app) 12 | 13 | -------------------------------------------------------------------------------- /examples/django/1.2.7/README: -------------------------------------------------------------------------------- 1 | Hello, Django! 2 | -------------------------------------------------------------------------------- /examples/django/1.2.7/config.yaml: -------------------------------------------------------------------------------- 1 | name: pylabs 2 | version: 9 3 | -------------------------------------------------------------------------------- /examples/django/1.2.7/index.wsgi: -------------------------------------------------------------------------------- 1 | import sys 2 | import os.path 3 | 4 | # manage.py is automatically created in each Django project. manage.py is a thin 5 | # wrapper around django-admin.py that takes care of two things for you before 6 | # delegating to django-admin.py: 7 | # 8 | # It puts your project's package on sys.path. 9 | # It sets the DJANGO_SETTINGS_MODULE environment variable so that it points to 10 | # your project's settings.py file. 11 | # 12 | # ref: https://docs.djangoproject.com/en/1.4/ref/django-admin/ 13 | 14 | os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' 15 | sys.path.append(os.path.join(os.path.dirname(__file__), 'mysite')) 16 | 17 | import sae 18 | import django.core.handlers.wsgi 19 | 20 | application = sae.create_wsgi_app(django.core.handlers.wsgi.WSGIHandler()) 21 | -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/mysite/__init__.py -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/mysite/demo/__init__.py -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/demo/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | class Demo(models.Model): 5 | text = models.CharField(max_length=256) 6 | 7 | -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/demo/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates two different styles of tests (one doctest and one 3 | unittest). These will both pass when you run "manage.py test". 4 | 5 | Replace these with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | class SimpleTest(TestCase): 11 | def test_basic_addition(self): 12 | """ 13 | Tests that 1 + 1 always equals 2. 14 | """ 15 | self.failUnlessEqual(1 + 1, 2) 16 | 17 | __test__ = {"doctest": """ 18 | Another way to test that 1 + 1 is equal to 2. 19 | 20 | >>> 1 + 1 == 2 21 | True 22 | """} 23 | 24 | -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/demo/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | 3 | from django.http import HttpResponse 4 | from django.template import Template, Context 5 | from django.core.context_processors import csrf 6 | from demo.models import Demo 7 | 8 | def showdemo(request): 9 | if request.method == 'POST': 10 | d = Demo(text=request.POST.get('text', '')) 11 | d.save() 12 | 13 | messages = Demo.objects.all() 14 | t = Template(""" 15 | {{ xxxx }} 16 | {% for m in messages %} 17 |

{{ m.text }}

18 | {% endfor %} 19 |
{% csrf_token %} 20 |
21 |
22 |
23 | """); 24 | d = {'messages': messages} 25 | d.update(csrf(request)) 26 | 27 | return HttpResponse(t.render(Context(d))) 28 | 29 | -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from django.core.management import execute_manager 3 | try: 4 | import settings # Assumed to be in the same directory. 5 | except ImportError: 6 | import sys 7 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) 8 | sys.exit(1) 9 | 10 | if __name__ == "__main__": 11 | execute_manager(settings) 12 | -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for mysite project. 2 | 3 | DEBUG = True 4 | TEMPLATE_DEBUG = DEBUG 5 | 6 | ADMINS = ( 7 | # ('Your Name', 'your_email@domain.com'), 8 | ) 9 | 10 | MANAGERS = ADMINS 11 | 12 | from sae.const import (MYSQL_HOST, MYSQL_HOST_S, 13 | MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB 14 | ) 15 | 16 | DATABASES = { 17 | 'default': { 18 | 'ENGINE': 'mysql', 19 | 'NAME': MYSQL_DB, 20 | 'USER': MYSQL_USER, 21 | 'PASSWORD': MYSQL_PASS, 22 | 'HOST': MYSQL_HOST, 23 | 'PORT': MYSQL_PORT, 24 | } 25 | } 26 | 27 | # Local time zone for this installation. Choices can be found here: 28 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 29 | # although not all choices may be available on all operating systems. 30 | # On Unix systems, a value of None will cause Django to use the same 31 | # timezone as the operating system. 32 | # If running in a Windows environment this must be set to the same as your 33 | # system time zone. 34 | TIME_ZONE = 'America/Chicago' 35 | 36 | # Language code for this installation. All choices can be found here: 37 | # http://www.i18nguy.com/unicode/language-identifiers.html 38 | LANGUAGE_CODE = 'en-us' 39 | 40 | SITE_ID = 1 41 | 42 | # If you set this to False, Django will make some optimizations so as not 43 | # to load the internationalization machinery. 44 | USE_I18N = True 45 | 46 | # If you set this to False, Django will not format dates, numbers and 47 | # calendars according to the current locale 48 | USE_L10N = True 49 | 50 | # Absolute filesystem path to the directory that will hold user-uploaded files. 51 | # Example: "/home/media/media.lawrence.com/" 52 | MEDIA_ROOT = '' 53 | 54 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 55 | # trailing slash if there is a path component (optional in other cases). 56 | # Examples: "http://media.lawrence.com", "http://example.com/media/" 57 | MEDIA_URL = '' 58 | 59 | # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a 60 | # trailing slash. 61 | # Examples: "http://foo.com/media/", "/media/". 62 | ADMIN_MEDIA_PREFIX = '/static/' 63 | 64 | # Make this unique, and don't share it with anybody. 65 | SECRET_KEY = 'xr45ymz8fs5wn*039+l462qwg7)7_yg$u7g6osv*3pynsr3#0#' 66 | 67 | # List of callables that know how to import templates from various sources. 68 | TEMPLATE_LOADERS = ( 69 | 'django.template.loaders.filesystem.Loader', 70 | 'django.template.loaders.app_directories.Loader', 71 | # 'django.template.loaders.eggs.Loader', 72 | ) 73 | 74 | MIDDLEWARE_CLASSES = ( 75 | 'django.middleware.common.CommonMiddleware', 76 | 'django.contrib.sessions.middleware.SessionMiddleware', 77 | 'django.middleware.csrf.CsrfViewMiddleware', 78 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 79 | 'django.contrib.messages.middleware.MessageMiddleware', 80 | ) 81 | 82 | ROOT_URLCONF = 'mysite.urls' 83 | 84 | TEMPLATE_DIRS = ( 85 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 86 | # Always use forward slashes, even on Windows. 87 | # Don't forget to use absolute paths, not relative paths. 88 | ) 89 | 90 | INSTALLED_APPS = ( 91 | 'django.contrib.auth', 92 | 'django.contrib.contenttypes', 93 | 'django.contrib.sessions', 94 | 'django.contrib.sites', 95 | 'django.contrib.messages', 96 | 'mysite.demo', 97 | # Uncomment the next line to enable the admin: 98 | 'django.contrib.admin', 99 | # Uncomment the next line to enable admin documentation: 100 | # 'django.contrib.admindocs', 101 | ) 102 | -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | 3 | # Uncomment the next two lines to enable the admin: 4 | from django.contrib import admin 5 | admin.autodiscover() 6 | 7 | urlpatterns = patterns('', 8 | # Example: 9 | # (r'^mysite/', include('mysite.foo.urls')), 10 | (r'^$', 'mysite.views.hello'), 11 | (r'^demo/$', 'mysite.demo.views.showdemo'), 12 | 13 | # Uncomment the admin/doc line below to enable admin documentation: 14 | # (r'^admin/doc/', include('django.contrib.admindocs.urls')), 15 | 16 | # Uncomment the next line to enable the admin: 17 | (r'^admin/', include(admin.site.urls)), 18 | ) 19 | -------------------------------------------------------------------------------- /examples/django/1.2.7/mysite/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | 3 | def hello(request): 4 | return HttpResponse("Hello, world! - Django") 5 | 6 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/css/changelists.css: -------------------------------------------------------------------------------- 1 | /* CHANGELISTS */ 2 | 3 | #changelist { 4 | position: relative; 5 | width: 100%; 6 | } 7 | 8 | #changelist table { 9 | width: 100%; 10 | } 11 | 12 | .change-list .hiddenfields { display:none; } 13 | 14 | .change-list .filtered table { 15 | border-right: 1px solid #ddd; 16 | } 17 | 18 | .change-list .filtered { 19 | min-height: 400px; 20 | } 21 | 22 | .change-list .filtered { 23 | background: white url(../img/admin/changelist-bg.gif) top right repeat-y !important; 24 | } 25 | 26 | .change-list .filtered table, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { 27 | margin-right: 160px !important; 28 | width: auto !important; 29 | } 30 | 31 | .change-list .filtered table tbody th { 32 | padding-right: 1em; 33 | } 34 | 35 | #changelist .toplinks { 36 | border-bottom: 1px solid #ccc !important; 37 | } 38 | 39 | #changelist .paginator { 40 | color: #666; 41 | border-top: 1px solid #eee; 42 | border-bottom: 1px solid #eee; 43 | background: white url(../img/admin/nav-bg.gif) 0 180% repeat-x; 44 | overflow: hidden; 45 | } 46 | 47 | .change-list .filtered .paginator { 48 | border-right: 1px solid #ddd; 49 | } 50 | 51 | /* CHANGELIST TABLES */ 52 | 53 | #changelist table thead th { 54 | white-space: nowrap; 55 | vertical-align: middle; 56 | } 57 | 58 | #changelist table thead th.action-checkbox-column { 59 | width: 1.5em; 60 | text-align: center; 61 | } 62 | 63 | #changelist table tbody td, #changelist table tbody th { 64 | border-left: 1px solid #ddd; 65 | } 66 | 67 | #changelist table tbody td:first-child, #changelist table tbody th:first-child { 68 | border-left: 0; 69 | border-right: 1px solid #ddd; 70 | } 71 | 72 | #changelist table tbody td.action-checkbox { 73 | text-align:center; 74 | } 75 | 76 | #changelist table tfoot { 77 | color: #666; 78 | } 79 | 80 | /* TOOLBAR */ 81 | 82 | #changelist #toolbar { 83 | padding: 3px; 84 | border-bottom: 1px solid #ddd; 85 | background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; 86 | color: #666; 87 | } 88 | 89 | #changelist #toolbar form input { 90 | font-size: 11px; 91 | padding: 1px 2px; 92 | } 93 | 94 | #changelist #toolbar form #searchbar { 95 | padding: 2px; 96 | } 97 | 98 | #changelist #changelist-search img { 99 | vertical-align: middle; 100 | } 101 | 102 | /* FILTER COLUMN */ 103 | 104 | #changelist-filter { 105 | position: absolute; 106 | top: 0; 107 | right: 0; 108 | z-index: 1000; 109 | width: 160px; 110 | border-left: 1px solid #ddd; 111 | background: #efefef; 112 | margin: 0; 113 | } 114 | 115 | #changelist-filter h2 { 116 | font-size: 11px; 117 | padding: 2px 5px; 118 | border-bottom: 1px solid #ddd; 119 | } 120 | 121 | #changelist-filter h3 { 122 | font-size: 12px; 123 | margin-bottom: 0; 124 | } 125 | 126 | #changelist-filter ul { 127 | padding-left: 0; 128 | margin-left: 10px; 129 | } 130 | 131 | #changelist-filter li { 132 | list-style-type: none; 133 | margin-left: 0; 134 | padding-left: 0; 135 | } 136 | 137 | #changelist-filter a { 138 | color: #999; 139 | } 140 | 141 | #changelist-filter a:hover { 142 | color: #036; 143 | } 144 | 145 | #changelist-filter li.selected { 146 | border-left: 5px solid #ccc; 147 | padding-left: 5px; 148 | margin-left: -10px; 149 | } 150 | 151 | #changelist-filter li.selected a { 152 | color: #5b80b2 !important; 153 | } 154 | 155 | /* DATE DRILLDOWN */ 156 | 157 | .change-list ul.toplinks { 158 | display: block; 159 | background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x; 160 | border-top: 1px solid white; 161 | float: left; 162 | padding: 0 !important; 163 | margin: 0 !important; 164 | width: 100%; 165 | } 166 | 167 | .change-list ul.toplinks li { 168 | float: left; 169 | width: 9em; 170 | padding: 3px 6px; 171 | font-weight: bold; 172 | list-style-type: none; 173 | } 174 | 175 | .change-list ul.toplinks .date-back a { 176 | color: #999; 177 | } 178 | 179 | .change-list ul.toplinks .date-back a:hover { 180 | color: #036; 181 | } 182 | 183 | /* PAGINATOR */ 184 | 185 | .paginator { 186 | font-size: 11px; 187 | padding-top: 10px; 188 | padding-bottom: 10px; 189 | line-height: 22px; 190 | margin: 0; 191 | border-top: 1px solid #ddd; 192 | } 193 | 194 | .paginator a:link, .paginator a:visited { 195 | padding: 2px 6px; 196 | border: solid 1px #ccc; 197 | background: white; 198 | text-decoration: none; 199 | } 200 | 201 | .paginator a.showall { 202 | padding: 0 !important; 203 | border: none !important; 204 | } 205 | 206 | .paginator a.showall:hover { 207 | color: #036 !important; 208 | background: transparent !important; 209 | } 210 | 211 | .paginator .end { 212 | border-width: 2px !important; 213 | margin-right: 6px; 214 | } 215 | 216 | .paginator .this-page { 217 | padding: 2px 6px; 218 | font-weight: bold; 219 | font-size: 13px; 220 | vertical-align: top; 221 | } 222 | 223 | .paginator a:hover { 224 | color: white; 225 | background: #5b80b2; 226 | border-color: #036; 227 | } 228 | 229 | /* ACTIONS */ 230 | 231 | .filtered .actions { 232 | margin-right: 160px !important; 233 | border-right: 1px solid #ddd; 234 | } 235 | 236 | #changelist table input { 237 | margin: 0; 238 | } 239 | 240 | #changelist table tbody tr.selected { 241 | background-color: #FFFFCC; 242 | } 243 | 244 | #changelist .actions { 245 | color: #999; 246 | padding: 3px; 247 | border-top: 1px solid #fff; 248 | border-bottom: 1px solid #ddd; 249 | background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x; 250 | } 251 | 252 | #changelist .actions.selected { 253 | background: #fffccf; 254 | border-top: 1px solid #fffee8; 255 | border-bottom: 1px solid #edecd6; 256 | } 257 | 258 | #changelist .actions span.all, 259 | #changelist .actions span.action-counter, 260 | #changelist .actions span.clear, 261 | #changelist .actions span.question { 262 | font-size: 11px; 263 | margin: 0 0.5em; 264 | display: none; 265 | } 266 | 267 | #changelist .actions:last-child { 268 | border-bottom: none; 269 | } 270 | 271 | #changelist .actions select { 272 | border: 1px solid #aaa; 273 | margin-left: 0.5em; 274 | padding: 1px 2px; 275 | } 276 | 277 | #changelist .actions label { 278 | font-size: 11px; 279 | margin-left: 0.5em; 280 | } 281 | 282 | #changelist #action-toggle { 283 | display: none; 284 | } 285 | 286 | #changelist .actions .button { 287 | font-size: 11px; 288 | padding: 1px 2px; 289 | } 290 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/css/dashboard.css: -------------------------------------------------------------------------------- 1 | /* DASHBOARD */ 2 | 3 | .dashboard .module table th { 4 | width: 100%; 5 | } 6 | 7 | .dashboard .module table td { 8 | white-space: nowrap; 9 | } 10 | 11 | .dashboard .module table td a { 12 | display: block; 13 | padding-right: .6em; 14 | } 15 | 16 | /* RECENT ACTIONS MODULE */ 17 | 18 | .module ul.actionlist { 19 | margin-left: 0; 20 | } 21 | 22 | ul.actionlist li { 23 | list-style-type: none; 24 | } 25 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/css/ie.css: -------------------------------------------------------------------------------- 1 | /* IE 6 & 7 */ 2 | 3 | /* Proper fixed width for dashboard in IE6 */ 4 | 5 | .dashboard #content { 6 | *width: 768px; 7 | } 8 | 9 | .dashboard #content-main { 10 | *width: 535px; 11 | } 12 | 13 | /* IE 6 ONLY */ 14 | 15 | /* Keep header from flowing off the page */ 16 | 17 | #container { 18 | _position: static; 19 | } 20 | 21 | /* Put the right sidebars back on the page */ 22 | 23 | .colMS #content-related { 24 | _margin-right: 0; 25 | _margin-left: 10px; 26 | _position: static; 27 | } 28 | 29 | /* Put the left sidebars back on the page */ 30 | 31 | .colSM #content-related { 32 | _margin-right: 10px; 33 | _margin-left: -115px; 34 | _position: static; 35 | } 36 | 37 | .form-row { 38 | _height: 1%; 39 | } 40 | 41 | /* Fix right margin for changelist filters in IE6 */ 42 | 43 | #changelist-filter ul { 44 | _margin-right: -10px; 45 | } 46 | 47 | /* IE ignores min-height, but treats height as if it were min-height */ 48 | 49 | .change-list .filtered { 50 | _height: 400px; 51 | } 52 | 53 | /* IE doesn't know alpha transparency in PNGs */ 54 | 55 | .inline-deletelink { 56 | background: transparent url(../img/admin/inline-delete-8bit.png) no-repeat; 57 | } -------------------------------------------------------------------------------- /examples/django/1.2.7/static/css/login.css: -------------------------------------------------------------------------------- 1 | /* LOGIN FORM */ 2 | 3 | body.login { 4 | background: #eee; 5 | } 6 | 7 | .login #container { 8 | background: white; 9 | border: 1px solid #ccc; 10 | width: 28em; 11 | min-width: 300px; 12 | margin-left: auto; 13 | margin-right: auto; 14 | margin-top: 100px; 15 | } 16 | 17 | .login #content-main { 18 | width: 100%; 19 | } 20 | 21 | .login form { 22 | margin-top: 1em; 23 | } 24 | 25 | .login .form-row { 26 | padding: 4px 0; 27 | float: left; 28 | width: 100%; 29 | } 30 | 31 | .login .form-row label { 32 | float: left; 33 | width: 9em; 34 | padding-right: 0.5em; 35 | line-height: 2em; 36 | text-align: right; 37 | font-size: 1em; 38 | color: #333; 39 | } 40 | 41 | .login .form-row #id_username, .login .form-row #id_password { 42 | width: 14em; 43 | } 44 | 45 | .login span.help { 46 | font-size: 10px; 47 | display: block; 48 | } 49 | 50 | .login .submit-row { 51 | clear: both; 52 | padding: 1em 0 0 9.4em; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/css/rtl.css: -------------------------------------------------------------------------------- 1 | body { 2 | direction: rtl; 3 | } 4 | 5 | /* LOGIN */ 6 | 7 | .login .form-row { 8 | float: right; 9 | } 10 | 11 | .login .form-row label { 12 | float: right; 13 | padding-left: 0.5em; 14 | padding-right: 0; 15 | text-align: left; 16 | } 17 | 18 | .login .submit-row { 19 | clear: both; 20 | padding: 1em 9.4em 0 0; 21 | } 22 | 23 | /* GLOBAL */ 24 | 25 | th { 26 | text-align: right; 27 | } 28 | 29 | .module h2, .module caption { 30 | text-align: right; 31 | } 32 | 33 | .addlink, .changelink { 34 | padding-left: 0px; 35 | padding-right: 12px; 36 | background-position: 100% 0.2em; 37 | } 38 | 39 | .deletelink { 40 | padding-left: 0px; 41 | padding-right: 12px; 42 | background-position: 100% 0.25em; 43 | } 44 | 45 | .object-tools { 46 | float: left; 47 | } 48 | 49 | /* LAYOUT */ 50 | 51 | #user-tools { 52 | right: auto; 53 | left: 0; 54 | text-align: left; 55 | } 56 | 57 | div.breadcrumbs { 58 | text-align: right; 59 | } 60 | 61 | #content-main { 62 | float: right; 63 | } 64 | 65 | #content-related { 66 | float: left; 67 | margin-left: -19em; 68 | margin-right: auto; 69 | } 70 | 71 | .colMS { 72 | margin-left: 20em !important; 73 | margin-right: 10px !important; 74 | } 75 | 76 | /* dashboard styles */ 77 | 78 | .dashboard .module table td a { 79 | padding-left: .6em; 80 | padding-right: 12px; 81 | } 82 | 83 | /* changelists styles */ 84 | 85 | .change-list ul.toplinks li { 86 | float: right; 87 | } 88 | 89 | .change-list .filtered { 90 | background: white url(../img/admin/changelist-bg_rtl.gif) top left repeat-y !important; 91 | } 92 | 93 | .change-list .filtered table { 94 | border-left: 1px solid #ddd; 95 | border-right: 0px none; 96 | } 97 | 98 | #changelist-filter { 99 | right: auto; 100 | left: 0; 101 | border-left: 0px none; 102 | border-right: 1px solid #ddd; 103 | } 104 | 105 | .change-list .filtered table, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { 106 | margin-right: 0px !important; 107 | margin-left: 160px !important; 108 | } 109 | 110 | #changelist-filter li.selected { 111 | border-left: 0px none; 112 | padding-left: 0px; 113 | margin-left: 0; 114 | border-right: 5px solid #ccc; 115 | padding-right: 5px; 116 | margin-right: -10px; 117 | } 118 | 119 | .filtered .actions { 120 | border-left:1px solid #DDDDDD; 121 | margin-left:160px !important; 122 | border-right: 0 none; 123 | margin-right:0 !important; 124 | } 125 | 126 | /* FORMS */ 127 | 128 | .aligned label { 129 | padding: 0 0 3px 1em; 130 | float: right; 131 | } 132 | 133 | .submit-row { 134 | text-align: left 135 | } 136 | 137 | .submit-row p.deletelink-box { 138 | float: right; 139 | } 140 | 141 | .submit-row .deletelink { 142 | background: url(../img/admin/icon_deletelink.gif) 0 50% no-repeat; 143 | padding-right: 14px; 144 | } 145 | 146 | .vDateField, .vTimeField { 147 | margin-left: 2px; 148 | } 149 | 150 | form ul.inline li { 151 | float: right; 152 | padding-right: 0; 153 | padding-left: 7px; 154 | } 155 | 156 | input[type=submit].default, .submit-row input.default { 157 | float: left; 158 | } 159 | 160 | fieldset .field-box { 161 | float: right; 162 | margin-left: 20px; 163 | } 164 | 165 | .errorlist li { 166 | background-position: 100% .3em; 167 | padding: 4px 25px 4px 5px; 168 | } 169 | 170 | .errornote { 171 | background-position: 100% .3em; 172 | padding: 4px 25px 4px 5px; 173 | } 174 | 175 | /* WIDGETS */ 176 | 177 | .calendarnav-previous { 178 | top: 0; 179 | left: auto; 180 | right: 0; 181 | } 182 | 183 | .calendarnav-next { 184 | top: 0; 185 | right: auto; 186 | left: 0; 187 | } 188 | 189 | .calendar caption, .calendarbox h2 { 190 | text-align: center; 191 | } 192 | 193 | .selector { 194 | float: right; 195 | } 196 | 197 | .selector .selector-filter { 198 | text-align: right; 199 | } 200 | 201 | .inline-deletelink { 202 | float: left; 203 | } 204 | 205 | /* MISC */ 206 | 207 | .inline-related h2, .inline-group h2 { 208 | text-align: right 209 | } 210 | 211 | .inline-related h3 span.delete { 212 | padding-right: 20px; 213 | padding-left: inherit; 214 | left: 10px; 215 | right: inherit; 216 | } 217 | 218 | .inline-related h3 span.delete label { 219 | margin-left: inherit; 220 | margin-right: 2px; 221 | } 222 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/arrow-down.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/arrow-down.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/arrow-up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/arrow-up.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/changelist-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/changelist-bg.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/changelist-bg_rtl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/changelist-bg_rtl.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/chooser-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/chooser-bg.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/chooser_stacked-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/chooser_stacked-bg.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/default-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/default-bg-reverse.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/default-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/default-bg.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/deleted-overlay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/deleted-overlay.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon-no.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon-no.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon-unknown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon-unknown.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon-yes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon-yes.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_addlink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_addlink.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_alert.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_alert.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_calendar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_calendar.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_changelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_changelink.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_clock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_clock.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_deletelink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_deletelink.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_error.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_searchbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_searchbox.png -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/icon_success.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/icon_success.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/inline-delete-8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/inline-delete-8bit.png -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/inline-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/inline-delete.png -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/inline-restore-8bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/inline-restore-8bit.png -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/inline-restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/inline-restore.png -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/inline-splitter-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/inline-splitter-bg.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/nav-bg-grabber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/nav-bg-grabber.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/nav-bg-reverse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/nav-bg-reverse.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/nav-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/nav-bg.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/selector-add.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/selector-add.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/selector-addall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/selector-addall.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/selector-remove.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/selector-remove.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/selector-removeall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/selector-removeall.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/selector-search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/selector-search.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/selector_stacked-add.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/selector_stacked-add.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/selector_stacked-remove.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/selector_stacked-remove.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/tool-left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/tool-left.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/tool-left_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/tool-left_over.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/tool-right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/tool-right.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/tool-right_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/tool-right_over.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/tooltag-add.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/tooltag-add.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/tooltag-add_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/tooltag-add_over.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/tooltag-arrowright.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/tooltag-arrowright.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/admin/tooltag-arrowright_over.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/admin/tooltag-arrowright_over.gif -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/gis/move_vertex_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/gis/move_vertex_off.png -------------------------------------------------------------------------------- /examples/django/1.2.7/static/img/gis/move_vertex_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.2.7/static/img/gis/move_vertex_on.png -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/LICENSE-JQUERY.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 John Resig, http://jquery.com/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/SelectBox.js: -------------------------------------------------------------------------------- 1 | var SelectBox = { 2 | cache: new Object(), 3 | init: function(id) { 4 | var box = document.getElementById(id); 5 | var node; 6 | SelectBox.cache[id] = new Array(); 7 | var cache = SelectBox.cache[id]; 8 | for (var i = 0; (node = box.options[i]); i++) { 9 | cache.push({value: node.value, text: node.text, displayed: 1}); 10 | } 11 | }, 12 | redisplay: function(id) { 13 | // Repopulate HTML select box from cache 14 | var box = document.getElementById(id); 15 | box.options.length = 0; // clear all options 16 | for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) { 17 | var node = SelectBox.cache[id][i]; 18 | if (node.displayed) { 19 | box.options[box.options.length] = new Option(node.text, node.value, false, false); 20 | } 21 | } 22 | }, 23 | filter: function(id, text) { 24 | // Redisplay the HTML select box, displaying only the choices containing ALL 25 | // the words in text. (It's an AND search.) 26 | var tokens = text.toLowerCase().split(/\s+/); 27 | var node, token; 28 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 29 | node.displayed = 1; 30 | for (var j = 0; (token = tokens[j]); j++) { 31 | if (node.text.toLowerCase().indexOf(token) == -1) { 32 | node.displayed = 0; 33 | } 34 | } 35 | } 36 | SelectBox.redisplay(id); 37 | }, 38 | delete_from_cache: function(id, value) { 39 | var node, delete_index = null; 40 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 41 | if (node.value == value) { 42 | delete_index = i; 43 | break; 44 | } 45 | } 46 | var j = SelectBox.cache[id].length - 1; 47 | for (var i = delete_index; i < j; i++) { 48 | SelectBox.cache[id][i] = SelectBox.cache[id][i+1]; 49 | } 50 | SelectBox.cache[id].length--; 51 | }, 52 | add_to_cache: function(id, option) { 53 | SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1}); 54 | }, 55 | cache_contains: function(id, value) { 56 | // Check if an item is contained in the cache 57 | var node; 58 | for (var i = 0; (node = SelectBox.cache[id][i]); i++) { 59 | if (node.value == value) { 60 | return true; 61 | } 62 | } 63 | return false; 64 | }, 65 | move: function(from, to) { 66 | var from_box = document.getElementById(from); 67 | var to_box = document.getElementById(to); 68 | var option; 69 | for (var i = 0; (option = from_box.options[i]); i++) { 70 | if (option.selected && SelectBox.cache_contains(from, option.value)) { 71 | SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1}); 72 | SelectBox.delete_from_cache(from, option.value); 73 | } 74 | } 75 | SelectBox.redisplay(from); 76 | SelectBox.redisplay(to); 77 | }, 78 | move_all: function(from, to) { 79 | var from_box = document.getElementById(from); 80 | var to_box = document.getElementById(to); 81 | var option; 82 | for (var i = 0; (option = from_box.options[i]); i++) { 83 | if (SelectBox.cache_contains(from, option.value)) { 84 | SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1}); 85 | SelectBox.delete_from_cache(from, option.value); 86 | } 87 | } 88 | SelectBox.redisplay(from); 89 | SelectBox.redisplay(to); 90 | }, 91 | sort: function(id) { 92 | SelectBox.cache[id].sort( function(a, b) { 93 | a = a.text.toLowerCase(); 94 | b = b.text.toLowerCase(); 95 | try { 96 | if (a > b) return 1; 97 | if (a < b) return -1; 98 | } 99 | catch (e) { 100 | // silently fail on IE 'unknown' exception 101 | } 102 | return 0; 103 | } ); 104 | }, 105 | select_all: function(id) { 106 | var box = document.getElementById(id); 107 | for (var i = 0; i < box.options.length; i++) { 108 | box.options[i].selected = 'selected'; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/actions.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $.fn.actions = function(opts) { 3 | var options = $.extend({}, $.fn.actions.defaults, opts); 4 | var actionCheckboxes = $(this); 5 | var list_editable_changed = false; 6 | checker = function(checked) { 7 | if (checked) { 8 | showQuestion(); 9 | } else { 10 | reset(); 11 | } 12 | $(actionCheckboxes).attr("checked", checked) 13 | .parent().parent().toggleClass(options.selectedClass, checked); 14 | } 15 | updateCounter = function() { 16 | var sel = $(actionCheckboxes).filter(":checked").length; 17 | $(options.counterContainer).html(interpolate( 18 | ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), { 19 | sel: sel, 20 | cnt: _actions_icnt 21 | }, true)); 22 | $(options.allToggle).attr("checked", function() { 23 | if (sel == actionCheckboxes.length) { 24 | value = true; 25 | showQuestion(); 26 | } else { 27 | value = false; 28 | clearAcross(); 29 | } 30 | return value; 31 | }); 32 | } 33 | showQuestion = function() { 34 | $(options.acrossClears).hide(); 35 | $(options.acrossQuestions).show(); 36 | $(options.allContainer).hide(); 37 | } 38 | showClear = function() { 39 | $(options.acrossClears).show(); 40 | $(options.acrossQuestions).hide(); 41 | $(options.actionContainer).toggleClass(options.selectedClass); 42 | $(options.allContainer).show(); 43 | $(options.counterContainer).hide(); 44 | } 45 | reset = function() { 46 | $(options.acrossClears).hide(); 47 | $(options.acrossQuestions).hide(); 48 | $(options.allContainer).hide(); 49 | $(options.counterContainer).show(); 50 | } 51 | clearAcross = function() { 52 | reset(); 53 | $(options.acrossInput).val(0); 54 | $(options.actionContainer).removeClass(options.selectedClass); 55 | } 56 | // Show counter by default 57 | $(options.counterContainer).show(); 58 | // Check state of checkboxes and reinit state if needed 59 | $(this).filter(":checked").each(function(i) { 60 | $(this).parent().parent().toggleClass(options.selectedClass); 61 | updateCounter(); 62 | if ($(options.acrossInput).val() == 1) { 63 | showClear(); 64 | } 65 | }); 66 | $(options.allToggle).show().click(function() { 67 | checker($(this).attr("checked")); 68 | updateCounter(); 69 | }); 70 | $("div.actions span.question a").click(function(event) { 71 | event.preventDefault(); 72 | $(options.acrossInput).val(1); 73 | showClear(); 74 | }); 75 | $("div.actions span.clear a").click(function(event) { 76 | event.preventDefault(); 77 | $(options.allToggle).attr("checked", false); 78 | clearAcross(); 79 | checker(0); 80 | updateCounter(); 81 | }); 82 | lastChecked = null; 83 | $(actionCheckboxes).click(function(event) { 84 | if (!event) { var event = window.event; } 85 | var target = event.target ? event.target : event.srcElement; 86 | if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey == true) { 87 | var inrange = false; 88 | $(lastChecked).attr("checked", target.checked) 89 | .parent().parent().toggleClass(options.selectedClass, target.checked); 90 | $(actionCheckboxes).each(function() { 91 | if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) { 92 | inrange = (inrange) ? false : true; 93 | } 94 | if (inrange) { 95 | $(this).attr("checked", target.checked) 96 | .parent().parent().toggleClass(options.selectedClass, target.checked); 97 | } 98 | }); 99 | } 100 | $(target).parent().parent().toggleClass(options.selectedClass, target.checked); 101 | lastChecked = target; 102 | updateCounter(); 103 | }); 104 | $('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() { 105 | list_editable_changed = true; 106 | }); 107 | $('form#changelist-form button[name="index"]').click(function(event) { 108 | if (list_editable_changed) { 109 | return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); 110 | } 111 | }); 112 | $('form#changelist-form input[name="_save"]').click(function(event) { 113 | var action_changed = false; 114 | $('div.actions select option:selected').each(function() { 115 | if ($(this).val()) { 116 | action_changed = true; 117 | } 118 | }); 119 | if (action_changed) { 120 | if (list_editable_changed) { 121 | return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")); 122 | } else { 123 | return confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button.")); 124 | } 125 | } 126 | }); 127 | } 128 | /* Setup plugin defaults */ 129 | $.fn.actions.defaults = { 130 | actionContainer: "div.actions", 131 | counterContainer: "span.action-counter", 132 | allContainer: "div.actions span.all", 133 | acrossInput: "div.actions input.select-across", 134 | acrossQuestions: "div.actions span.question", 135 | acrossClears: "div.actions span.clear", 136 | allToggle: "#action-toggle", 137 | selectedClass: "selected" 138 | } 139 | })(django.jQuery); 140 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/actions.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a.fn.actions=function(h){var b=a.extend({},a.fn.actions.defaults,h),e=a(this),f=false;checker=function(c){c?showQuestion():reset();a(e).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)};updateCounter=function(){var c=a(e).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},true));a(b.allToggle).attr("checked",function(){if(c==e.length){value=true;showQuestion()}else{value= 2 | false;clearAcross()}return value})};showQuestion=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()};showClear=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()};reset=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()};clearAcross=function(){reset();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)}; 3 | a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);updateCounter();a(b.acrossInput).val()==1&&showClear()});a(b.allToggle).show().click(function(){checker(a(this).attr("checked"));updateCounter()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);showClear()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",false);clearAcross();checker(0); 4 | updateCounter()});lastChecked=null;a(e).click(function(c){if(!c)c=window.event;var d=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&c.shiftKey==true){var g=false;a(lastChecked).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(e).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))g=g?false:true;g&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass, 5 | d.checked);lastChecked=d;updateCounter()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){f=true});a('form#changelist-form button[name="index"]').click(function(){if(f)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var c=false;a("div.actions select option:selected").each(function(){if(a(this).val())c= 6 | true});if(c)return f?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across", 7 | acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery); 8 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/admin/RelatedObjectLookups.js: -------------------------------------------------------------------------------- 1 | // Handles related-objects functionality: lookup link for raw_id_fields 2 | // and Add Another links. 3 | 4 | function html_unescape(text) { 5 | // Unescape a string that was escaped using django.utils.html.escape. 6 | text = text.replace(/</g, '<'); 7 | text = text.replace(/>/g, '>'); 8 | text = text.replace(/"/g, '"'); 9 | text = text.replace(/'/g, "'"); 10 | text = text.replace(/&/g, '&'); 11 | return text; 12 | } 13 | 14 | // IE doesn't accept periods or dashes in the window name, but the element IDs 15 | // we use to generate popup window names may contain them, therefore we map them 16 | // to allowed characters in a reversible way so that we can locate the correct 17 | // element when the popup window is dismissed. 18 | function id_to_windowname(text) { 19 | text = text.replace(/\./g, '__dot__'); 20 | text = text.replace(/\-/g, '__dash__'); 21 | return text; 22 | } 23 | 24 | function windowname_to_id(text) { 25 | text = text.replace(/__dot__/g, '.'); 26 | text = text.replace(/__dash__/g, '-'); 27 | return text; 28 | } 29 | 30 | function showRelatedObjectLookupPopup(triggeringLink) { 31 | var name = triggeringLink.id.replace(/^lookup_/, ''); 32 | name = id_to_windowname(name); 33 | var href; 34 | if (triggeringLink.href.search(/\?/) >= 0) { 35 | href = triggeringLink.href + '&pop=1'; 36 | } else { 37 | href = triggeringLink.href + '?pop=1'; 38 | } 39 | var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 40 | win.focus(); 41 | return false; 42 | } 43 | 44 | function dismissRelatedLookupPopup(win, chosenId) { 45 | var name = windowname_to_id(win.name); 46 | var elem = document.getElementById(name); 47 | if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 48 | elem.value += ',' + chosenId; 49 | } else { 50 | document.getElementById(name).value = chosenId; 51 | } 52 | win.close(); 53 | } 54 | 55 | function showAddAnotherPopup(triggeringLink) { 56 | var name = triggeringLink.id.replace(/^add_/, ''); 57 | name = id_to_windowname(name); 58 | href = triggeringLink.href 59 | if (href.indexOf('?') == -1) { 60 | href += '?_popup=1'; 61 | } else { 62 | href += '&_popup=1'; 63 | } 64 | var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); 65 | win.focus(); 66 | return false; 67 | } 68 | 69 | function dismissAddAnotherPopup(win, newId, newRepr) { 70 | // newId and newRepr are expected to have previously been escaped by 71 | // django.utils.html.escape. 72 | newId = html_unescape(newId); 73 | newRepr = html_unescape(newRepr); 74 | var name = windowname_to_id(win.name); 75 | var elem = document.getElementById(name); 76 | if (elem) { 77 | if (elem.nodeName == 'SELECT') { 78 | var o = new Option(newRepr, newId); 79 | elem.options[elem.options.length] = o; 80 | o.selected = true; 81 | } else if (elem.nodeName == 'INPUT') { 82 | if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) { 83 | elem.value += ',' + newId; 84 | } else { 85 | elem.value = newId; 86 | } 87 | } 88 | } else { 89 | var toId = name + "_to"; 90 | elem = document.getElementById(toId); 91 | var o = new Option(newRepr, newId); 92 | SelectBox.add_to_cache(toId, o); 93 | SelectBox.redisplay(toId); 94 | } 95 | win.close(); 96 | } 97 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/admin/ordering.js: -------------------------------------------------------------------------------- 1 | addEvent(window, 'load', reorder_init); 2 | 3 | var lis; 4 | var top = 0; 5 | var left = 0; 6 | var height = 30; 7 | 8 | function reorder_init() { 9 | lis = document.getElementsBySelector('ul#orderthese li'); 10 | var input = document.getElementsBySelector('input[name=order_]')[0]; 11 | setOrder(input.value.split(',')); 12 | input.disabled = true; 13 | draw(); 14 | // Now initialise the dragging behaviour 15 | var limit = (lis.length - 1) * height; 16 | for (var i = 0; i < lis.length; i++) { 17 | var li = lis[i]; 18 | var img = document.getElementById('handle'+li.id); 19 | li.style.zIndex = 1; 20 | Drag.init(img, li, left + 10, left + 10, top + 10, top + 10 + limit); 21 | li.onDragStart = startDrag; 22 | li.onDragEnd = endDrag; 23 | img.style.cursor = 'move'; 24 | } 25 | } 26 | 27 | function submitOrderForm() { 28 | var inputOrder = document.getElementsBySelector('input[name=order_]')[0]; 29 | inputOrder.value = getOrder(); 30 | inputOrder.disabled=false; 31 | } 32 | 33 | function startDrag() { 34 | this.style.zIndex = '10'; 35 | this.className = 'dragging'; 36 | } 37 | 38 | function endDrag(x, y) { 39 | this.style.zIndex = '1'; 40 | this.className = ''; 41 | // Work out how far along it has been dropped, using x co-ordinate 42 | var oldIndex = this.index; 43 | var newIndex = Math.round((y - 10 - top) / height); 44 | // 'Snap' to the correct position 45 | this.style.top = (10 + top + newIndex * height) + 'px'; 46 | this.index = newIndex; 47 | moveItem(oldIndex, newIndex); 48 | } 49 | 50 | function moveItem(oldIndex, newIndex) { 51 | // Swaps two items, adjusts the index and left co-ord for all others 52 | if (oldIndex == newIndex) { 53 | return; // Nothing to swap; 54 | } 55 | var direction, lo, hi; 56 | if (newIndex > oldIndex) { 57 | lo = oldIndex; 58 | hi = newIndex; 59 | direction = -1; 60 | } else { 61 | direction = 1; 62 | hi = oldIndex; 63 | lo = newIndex; 64 | } 65 | var lis2 = new Array(); // We will build the new order in this array 66 | for (var i = 0; i < lis.length; i++) { 67 | if (i < lo || i > hi) { 68 | // Position of items not between the indexes is unaffected 69 | lis2[i] = lis[i]; 70 | continue; 71 | } else if (i == newIndex) { 72 | lis2[i] = lis[oldIndex]; 73 | continue; 74 | } else { 75 | // Item is between the two indexes - move it along 1 76 | lis2[i] = lis[i - direction]; 77 | } 78 | } 79 | // Re-index everything 80 | reIndex(lis2); 81 | lis = lis2; 82 | draw(); 83 | // document.getElementById('hiddenOrder').value = getOrder(); 84 | document.getElementsBySelector('input[name=order_]')[0].value = getOrder(); 85 | } 86 | 87 | function reIndex(lis) { 88 | for (var i = 0; i < lis.length; i++) { 89 | lis[i].index = i; 90 | } 91 | } 92 | 93 | function draw() { 94 | for (var i = 0; i < lis.length; i++) { 95 | var li = lis[i]; 96 | li.index = i; 97 | li.style.position = 'absolute'; 98 | li.style.left = (10 + left) + 'px'; 99 | li.style.top = (10 + top + (i * height)) + 'px'; 100 | } 101 | } 102 | 103 | function getOrder() { 104 | var order = new Array(lis.length); 105 | for (var i = 0; i < lis.length; i++) { 106 | order[i] = lis[i].id.substring(1, 100); 107 | } 108 | return order.join(','); 109 | } 110 | 111 | function setOrder(id_list) { 112 | /* Set the current order to match the lsit of IDs */ 113 | var temp_lis = new Array(); 114 | for (var i = 0; i < id_list.length; i++) { 115 | var id = 'p' + id_list[i]; 116 | temp_lis[temp_lis.length] = document.getElementById(id); 117 | } 118 | reIndex(temp_lis); 119 | lis = temp_lis; 120 | draw(); 121 | } 122 | 123 | function addEvent(elm, evType, fn, useCapture) 124 | // addEvent and removeEvent 125 | // cross-browser event handling for IE5+, NS6 and Mozilla 126 | // By Scott Andrew 127 | { 128 | if (elm.addEventListener){ 129 | elm.addEventListener(evType, fn, useCapture); 130 | return true; 131 | } else if (elm.attachEvent){ 132 | var r = elm.attachEvent("on"+evType, fn); 133 | return r; 134 | } else { 135 | elm['on'+evType] = fn; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/calendar.js: -------------------------------------------------------------------------------- 1 | /* 2 | calendar.js - Calendar functions by Adrian Holovaty 3 | */ 4 | 5 | function removeChildren(a) { // "a" is reference to an object 6 | while (a.hasChildNodes()) a.removeChild(a.lastChild); 7 | } 8 | 9 | // quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]); 10 | function quickElement() { 11 | var obj = document.createElement(arguments[0]); 12 | if (arguments[2] != '' && arguments[2] != null) { 13 | var textNode = document.createTextNode(arguments[2]); 14 | obj.appendChild(textNode); 15 | } 16 | var len = arguments.length; 17 | for (var i = 3; i < len; i += 2) { 18 | obj.setAttribute(arguments[i], arguments[i+1]); 19 | } 20 | arguments[1].appendChild(obj); 21 | return obj; 22 | } 23 | 24 | // CalendarNamespace -- Provides a collection of HTML calendar-related helper functions 25 | var CalendarNamespace = { 26 | monthsOfYear: gettext('January February March April May June July August September October November December').split(' '), 27 | daysOfWeek: gettext('S M T W T F S').split(' '), 28 | firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')), 29 | isLeapYear: function(year) { 30 | return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0)); 31 | }, 32 | getDaysInMonth: function(month,year) { 33 | var days; 34 | if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) { 35 | days = 31; 36 | } 37 | else if (month==4 || month==6 || month==9 || month==11) { 38 | days = 30; 39 | } 40 | else if (month==2 && CalendarNamespace.isLeapYear(year)) { 41 | days = 29; 42 | } 43 | else { 44 | days = 28; 45 | } 46 | return days; 47 | }, 48 | draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999 49 | var today = new Date(); 50 | var todayDay = today.getDate(); 51 | var todayMonth = today.getMonth()+1; 52 | var todayYear = today.getFullYear(); 53 | var todayClass = ''; 54 | 55 | month = parseInt(month); 56 | year = parseInt(year); 57 | var calDiv = document.getElementById(div_id); 58 | removeChildren(calDiv); 59 | var calTable = document.createElement('table'); 60 | quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year); 61 | var tableBody = quickElement('tbody', calTable); 62 | 63 | // Draw days-of-week header 64 | var tableRow = quickElement('tr', tableBody); 65 | for (var i = 0; i < 7; i++) { 66 | quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]); 67 | } 68 | 69 | var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay(); 70 | var days = CalendarNamespace.getDaysInMonth(month, year); 71 | 72 | // Draw blanks before first of month 73 | tableRow = quickElement('tr', tableBody); 74 | for (var i = 0; i < startingPos; i++) { 75 | var _cell = quickElement('td', tableRow, ' '); 76 | _cell.style.backgroundColor = '#f3f3f3'; 77 | } 78 | 79 | // Draw days of month 80 | var currentDay = 1; 81 | for (var i = startingPos; currentDay <= days; i++) { 82 | if (i%7 == 0 && currentDay != 1) { 83 | tableRow = quickElement('tr', tableBody); 84 | } 85 | if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) { 86 | todayClass='today'; 87 | } else { 88 | todayClass=''; 89 | } 90 | var cell = quickElement('td', tableRow, '', 'class', todayClass); 91 | 92 | quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));'); 93 | currentDay++; 94 | } 95 | 96 | // Draw blanks after end of month (optional, but makes for valid code) 97 | while (tableRow.childNodes.length < 7) { 98 | var _cell = quickElement('td', tableRow, ' '); 99 | _cell.style.backgroundColor = '#f3f3f3'; 100 | } 101 | 102 | calDiv.appendChild(calTable); 103 | } 104 | } 105 | 106 | // Calendar -- A calendar instance 107 | function Calendar(div_id, callback) { 108 | // div_id (string) is the ID of the element in which the calendar will 109 | // be displayed 110 | // callback (string) is the name of a JavaScript function that will be 111 | // called with the parameters (year, month, day) when a day in the 112 | // calendar is clicked 113 | this.div_id = div_id; 114 | this.callback = callback; 115 | this.today = new Date(); 116 | this.currentMonth = this.today.getMonth() + 1; 117 | this.currentYear = this.today.getFullYear(); 118 | } 119 | Calendar.prototype = { 120 | drawCurrent: function() { 121 | CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback); 122 | }, 123 | drawDate: function(month, year) { 124 | this.currentMonth = month; 125 | this.currentYear = year; 126 | this.drawCurrent(); 127 | }, 128 | drawPreviousMonth: function() { 129 | if (this.currentMonth == 1) { 130 | this.currentMonth = 12; 131 | this.currentYear--; 132 | } 133 | else { 134 | this.currentMonth--; 135 | } 136 | this.drawCurrent(); 137 | }, 138 | drawNextMonth: function() { 139 | if (this.currentMonth == 12) { 140 | this.currentMonth = 1; 141 | this.currentYear++; 142 | } 143 | else { 144 | this.currentMonth++; 145 | } 146 | this.drawCurrent(); 147 | }, 148 | drawPreviousYear: function() { 149 | this.currentYear--; 150 | this.drawCurrent(); 151 | }, 152 | drawNextYear: function() { 153 | this.currentYear++; 154 | this.drawCurrent(); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/collapse.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $(document).ready(function() { 3 | // Add anchor tag for Show/Hide link 4 | $("fieldset.collapse").each(function(i, elem) { 5 | // Don't hide if fields in this fieldset have errors 6 | if ( $(elem).find("div.errors").length == 0 ) { 7 | $(elem).addClass("collapsed"); 8 | $(elem).find("h2").first().append(' (' + gettext("Show") + 10 | ')'); 11 | } 12 | }); 13 | // Add toggle to anchor tag 14 | $("fieldset.collapse a.collapse-toggle").toggle( 15 | function() { // Show 16 | $(this).text(gettext("Hide")); 17 | $(this).closest("fieldset").removeClass("collapsed"); 18 | return false; 19 | }, 20 | function() { // Hide 21 | $(this).text(gettext("Show")); 22 | $(this).closest("fieldset").addClass("collapsed"); 23 | return false; 24 | } 25 | ); 26 | }); 27 | })(django.jQuery); 28 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/collapse.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){if(a(b).find("div.errors").length==0){a(b).addClass("collapsed");a(b).find("h2").first().append(' ('+gettext("Show")+")")}});a("fieldset.collapse a.collapse-toggle").toggle(function(){a(this).text(gettext("Hide"));a(this).closest("fieldset").removeClass("collapsed");return false},function(){a(this).text(gettext("Show"));a(this).closest("fieldset").addClass("collapsed"); 2 | return false})})})(django.jQuery); 3 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/compress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import optparse 4 | import subprocess 5 | import sys 6 | 7 | here = os.path.dirname(__file__) 8 | 9 | def main(): 10 | usage = "usage: %prog [file1..fileN]" 11 | description = """With no file paths given this script will automatically 12 | compress all jQuery-based files of the admin app. Requires the Google Closure 13 | Compiler library and Java version 6 or later.""" 14 | parser = optparse.OptionParser(usage, description=description) 15 | parser.add_option("-c", dest="compiler", default="~/bin/compiler.jar", 16 | help="path to Closure Compiler jar file") 17 | parser.add_option("-v", "--verbose", 18 | action="store_true", dest="verbose") 19 | parser.add_option("-q", "--quiet", 20 | action="store_false", dest="verbose") 21 | (options, args) = parser.parse_args() 22 | 23 | compiler = os.path.expanduser(options.compiler) 24 | if not os.path.exists(compiler): 25 | sys.exit("Google Closure compiler jar file %s not found. Please use the -c option to specify the path." % compiler) 26 | 27 | if not args: 28 | if options.verbose: 29 | sys.stdout.write("No filenames given; defaulting to admin scripts\n") 30 | args = [os.path.join(here, f) for f in [ 31 | "actions.js", "collapse.js", "inlines.js", "prepopulate.js"]] 32 | 33 | for arg in args: 34 | if not arg.endswith(".js"): 35 | arg = arg + ".js" 36 | to_compress = os.path.expanduser(arg) 37 | if os.path.exists(to_compress): 38 | to_compress_min = "%s.min.js" % "".join(arg.rsplit(".js")) 39 | cmd = "java -jar %s --js %s --js_output_file %s" % (compiler, to_compress, to_compress_min) 40 | if options.verbose: 41 | sys.stdout.write("Running: %s\n" % cmd) 42 | subprocess.call(cmd.split()) 43 | else: 44 | sys.stdout.write("File %s not found. Sure it exists?\n" % to_compress) 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/inlines.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Django admin inlines 3 | * 4 | * Based on jQuery Formset 1.1 5 | * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com) 6 | * @requires jQuery 1.2.6 or later 7 | * 8 | * Copyright (c) 2009, Stanislaus Madueke 9 | * All rights reserved. 10 | * 11 | * Spiced up with Code from Zain Memon's GSoC project 2009 12 | * and modified for Django by Jannis Leidel 13 | * 14 | * Licensed under the New BSD License 15 | * See: http://www.opensource.org/licenses/bsd-license.php 16 | */ 17 | (function($) { 18 | $.fn.formset = function(opts) { 19 | var options = $.extend({}, $.fn.formset.defaults, opts); 20 | var updateElementIndex = function(el, prefix, ndx) { 21 | var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))"); 22 | var replacement = prefix + "-" + ndx; 23 | if ($(el).attr("for")) { 24 | $(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); 25 | } 26 | if (el.id) { 27 | el.id = el.id.replace(id_regex, replacement); 28 | } 29 | if (el.name) { 30 | el.name = el.name.replace(id_regex, replacement); 31 | } 32 | }; 33 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off"); 34 | var nextIndex = parseInt(totalForms.val()); 35 | var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off"); 36 | // only show the add button if we are allowed to add more items, 37 | // note that max_num = None translates to a blank string. 38 | var showAddButton = maxForms.val() == '' || (maxForms.val()-totalForms.val()) > 0; 39 | $(this).each(function(i) { 40 | $(this).not("." + options.emptyCssClass).addClass(options.formCssClass); 41 | }); 42 | if ($(this).length && showAddButton) { 43 | var addButton; 44 | if ($(this).attr("tagName") == "TR") { 45 | // If forms are laid out as table rows, insert the 46 | // "add" button in a new table row: 47 | var numCols = this.eq(0).children().length; 48 | $(this).parent().append('' + options.addText + ""); 49 | addButton = $(this).parent().find("tr:last a"); 50 | } else { 51 | // Otherwise, insert it immediately after the last form: 52 | $(this).filter(":last").after('"); 53 | addButton = $(this).filter(":last").next().find("a"); 54 | } 55 | addButton.click(function() { 56 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS"); 57 | var template = $("#" + options.prefix + "-empty"); 58 | var row = template.clone(true); 59 | row.removeClass(options.emptyCssClass) 60 | .addClass(options.formCssClass) 61 | .attr("id", options.prefix + "-" + nextIndex); 62 | if (row.is("tr")) { 63 | // If the forms are laid out in table rows, insert 64 | // the remove button into the last table cell: 65 | row.children(":last").append('
' + options.deleteText + "
"); 66 | } else if (row.is("ul") || row.is("ol")) { 67 | // If they're laid out as an ordered/unordered list, 68 | // insert an
  • after the last list item: 69 | row.append('
  • ' + options.deleteText + "
  • "); 70 | } else { 71 | // Otherwise, just insert the remove button as the 72 | // last child element of the form's container: 73 | row.children(":first").append('' + options.deleteText + ""); 74 | } 75 | row.find("*").each(function() { 76 | updateElementIndex(this, options.prefix, totalForms.val()); 77 | }); 78 | // Insert the new form when it has been fully edited 79 | row.insertBefore($(template)); 80 | // Update number of total forms 81 | $(totalForms).val(parseInt(totalForms.val()) + 1); 82 | nextIndex += 1; 83 | // Hide add button in case we've hit the max, except we want to add infinitely 84 | if ((maxForms.val() != '') && (maxForms.val()-totalForms.val()) <= 0) { 85 | addButton.parent().hide(); 86 | } 87 | // The delete button of each row triggers a bunch of other things 88 | row.find("a." + options.deleteCssClass).click(function() { 89 | // Remove the parent form containing this button: 90 | var row = $(this).parents("." + options.formCssClass); 91 | row.remove(); 92 | nextIndex -= 1; 93 | // If a post-delete callback was provided, call it with the deleted form: 94 | if (options.removed) { 95 | options.removed(row); 96 | } 97 | // Update the TOTAL_FORMS form count. 98 | var forms = $("." + options.formCssClass); 99 | $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); 100 | // Show add button again once we drop below max 101 | if ((maxForms.val() == '') || (maxForms.val()-forms.length) > 0) { 102 | addButton.parent().show(); 103 | } 104 | // Also, update names and ids for all remaining form controls 105 | // so they remain in sequence: 106 | for (var i=0, formCount=forms.length; i0;b(this).each(function(){b(this).not("."+ 2 | a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&g){var j;if(b(this).attr("tagName")=="TR"){g=this.eq(0).children().length;b(this).parent().append(''+a.addText+"");j=b(this).parent().find("tr:last a")}else{b(this).filter(":last").after('");j=b(this).filter(":last").next().find("a")}j.click(function(){var c=b("#id_"+ 3 | a.prefix+"-TOTAL_FORMS"),f=b("#"+a.prefix+"-empty"),d=f.clone(true);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);if(d.is("tr"))d.children(":last").append('");else d.is("ul")||d.is("ol")?d.append('
  • '+a.deleteText+"
  • "):d.children(":first").append(''+ 4 | a.deleteText+"");d.find("*").each(function(){k(this,a.prefix,c.val())});d.insertBefore(b(f));b(c).val(parseInt(c.val())+1);l+=1;h.val()!=""&&h.val()-c.val()<=0&&j.parent().hide();d.find("a."+a.deleteCssClass).click(function(){var e=b(this).parents("."+a.formCssClass);e.remove();l-=1;a.removed&&a.removed(e);e=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(e.length);if(h.val()==""||h.val()-e.length>0)j.parent().show();for(var i=0,m=e.length;i 0) { 25 | values.push($(field).val()); 26 | } 27 | }) 28 | field.val(URLify(values.join(' '), maxLength)); 29 | }; 30 | 31 | $(dependencies.join(',')).keyup(populate).change(populate).focus(populate); 32 | }); 33 | }; 34 | })(django.jQuery); 35 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/prepopulate.min.js: -------------------------------------------------------------------------------- 1 | (function(a){a.fn.prepopulate=function(d,g){return this.each(function(){var b=a(this);b.data("_changed",false);b.change(function(){b.data("_changed",true)});var c=function(){if(b.data("_changed")!=true){var e=[];a.each(d,function(h,f){a(f).val().length>0&&e.push(a(f).val())});b.val(URLify(e.join(" "),g))}};a(d.join(",")).keyup(c).change(c).focus(c)})}})(django.jQuery); 2 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/timeparse.js: -------------------------------------------------------------------------------- 1 | var timeParsePatterns = [ 2 | // 9 3 | { re: /^\d{1,2}$/i, 4 | handler: function(bits) { 5 | if (bits[0].length == 1) { 6 | return '0' + bits[0] + ':00'; 7 | } else { 8 | return bits[0] + ':00'; 9 | } 10 | } 11 | }, 12 | // 13:00 13 | { re: /^\d{2}[:.]\d{2}$/i, 14 | handler: function(bits) { 15 | return bits[0].replace('.', ':'); 16 | } 17 | }, 18 | // 9:00 19 | { re: /^\d[:.]\d{2}$/i, 20 | handler: function(bits) { 21 | return '0' + bits[0].replace('.', ':'); 22 | } 23 | }, 24 | // 3 am / 3 a.m. / 3am 25 | { re: /^(\d+)\s*([ap])(?:.?m.?)?$/i, 26 | handler: function(bits) { 27 | var hour = parseInt(bits[1]); 28 | if (hour == 12) { 29 | hour = 0; 30 | } 31 | if (bits[2].toLowerCase() == 'p') { 32 | if (hour == 12) { 33 | hour = 0; 34 | } 35 | return (hour + 12) + ':00'; 36 | } else { 37 | if (hour < 10) { 38 | return '0' + hour + ':00'; 39 | } else { 40 | return hour + ':00'; 41 | } 42 | } 43 | } 44 | }, 45 | // 3.30 am / 3:15 a.m. / 3.00am 46 | { re: /^(\d+)[.:](\d{2})\s*([ap]).?m.?$/i, 47 | handler: function(bits) { 48 | var hour = parseInt(bits[1]); 49 | var mins = parseInt(bits[2]); 50 | if (mins < 10) { 51 | mins = '0' + mins; 52 | } 53 | if (hour == 12) { 54 | hour = 0; 55 | } 56 | if (bits[3].toLowerCase() == 'p') { 57 | if (hour == 12) { 58 | hour = 0; 59 | } 60 | return (hour + 12) + ':' + mins; 61 | } else { 62 | if (hour < 10) { 63 | return '0' + hour + ':' + mins; 64 | } else { 65 | return hour + ':' + mins; 66 | } 67 | } 68 | } 69 | }, 70 | // noon 71 | { re: /^no/i, 72 | handler: function(bits) { 73 | return '12:00'; 74 | } 75 | }, 76 | // midnight 77 | { re: /^mid/i, 78 | handler: function(bits) { 79 | return '00:00'; 80 | } 81 | } 82 | ]; 83 | 84 | function parseTimeString(s) { 85 | for (var i = 0; i < timeParsePatterns.length; i++) { 86 | var re = timeParsePatterns[i].re; 87 | var handler = timeParsePatterns[i].handler; 88 | var bits = re.exec(s); 89 | if (bits) { 90 | return handler(bits); 91 | } 92 | } 93 | return s; 94 | } 95 | -------------------------------------------------------------------------------- /examples/django/1.2.7/static/js/urlify.js: -------------------------------------------------------------------------------- 1 | var LATIN_MAP = { 2 | 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç': 3 | 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 4 | 'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 5 | 'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U', 6 | 'Ý': 'Y', 'Þ': 'TH', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 'a', 'ä': 7 | 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 8 | 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 9 | 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u', 10 | 'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y' 11 | } 12 | var LATIN_SYMBOLS_MAP = { 13 | '©':'(c)' 14 | } 15 | var GREEK_MAP = { 16 | 'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8', 17 | 'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p', 18 | 'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w', 19 | 'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s', 20 | 'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i', 21 | 'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8', 22 | 'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P', 23 | 'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W', 24 | 'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I', 25 | 'Ϋ':'Y' 26 | } 27 | var TURKISH_MAP = { 28 | 'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U', 29 | 'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G' 30 | } 31 | var RUSSIAN_MAP = { 32 | 'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh', 33 | 'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o', 34 | 'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c', 35 | 'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu', 36 | 'я':'ya', 37 | 'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh', 38 | 'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O', 39 | 'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C', 40 | 'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu', 41 | 'Я':'Ya' 42 | } 43 | var UKRAINIAN_MAP = { 44 | 'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g' 45 | } 46 | var CZECH_MAP = { 47 | 'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u', 48 | 'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T', 49 | 'Ů':'U', 'Ž':'Z' 50 | } 51 | 52 | var POLISH_MAP = { 53 | 'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z', 54 | 'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'e', 'Ł':'L', 'Ń':'N', 'Ó':'o', 'Ś':'S', 55 | 'Ź':'Z', 'Ż':'Z' 56 | } 57 | 58 | var LATVIAN_MAP = { 59 | 'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n', 60 | 'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i', 61 | 'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z' 62 | } 63 | 64 | var ALL_DOWNCODE_MAPS=new Array() 65 | ALL_DOWNCODE_MAPS[0]=LATIN_MAP 66 | ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP 67 | ALL_DOWNCODE_MAPS[2]=GREEK_MAP 68 | ALL_DOWNCODE_MAPS[3]=TURKISH_MAP 69 | ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP 70 | ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP 71 | ALL_DOWNCODE_MAPS[6]=CZECH_MAP 72 | ALL_DOWNCODE_MAPS[7]=POLISH_MAP 73 | ALL_DOWNCODE_MAPS[8]=LATVIAN_MAP 74 | 75 | var Downcoder = new Object(); 76 | Downcoder.Initialize = function() 77 | { 78 | if (Downcoder.map) // already made 79 | return ; 80 | Downcoder.map ={} 81 | Downcoder.chars = '' ; 82 | for(var i in ALL_DOWNCODE_MAPS) 83 | { 84 | var lookup = ALL_DOWNCODE_MAPS[i] 85 | for (var c in lookup) 86 | { 87 | Downcoder.map[c] = lookup[c] ; 88 | Downcoder.chars += c ; 89 | } 90 | } 91 | Downcoder.regex = new RegExp('[' + Downcoder.chars + ']|[^' + Downcoder.chars + ']+','g') ; 92 | } 93 | 94 | downcode= function( slug ) 95 | { 96 | Downcoder.Initialize() ; 97 | var downcoded ="" 98 | var pieces = slug.match(Downcoder.regex); 99 | if(pieces) 100 | { 101 | for (var i = 0 ; i < pieces.length ; i++) 102 | { 103 | if (pieces[i].length == 1) 104 | { 105 | var mapped = Downcoder.map[pieces[i]] ; 106 | if (mapped != null) 107 | { 108 | downcoded+=mapped; 109 | continue ; 110 | } 111 | } 112 | downcoded+=pieces[i]; 113 | } 114 | } 115 | else 116 | { 117 | downcoded = slug; 118 | } 119 | return downcoded; 120 | } 121 | 122 | 123 | function URLify(s, num_chars) { 124 | // changes, e.g., "Petty theft" to "petty_theft" 125 | // remove all these words from the string before urlifying 126 | s = downcode(s); 127 | removelist = ["a", "an", "as", "at", "before", "but", "by", "for", "from", 128 | "is", "in", "into", "like", "of", "off", "on", "onto", "per", 129 | "since", "than", "the", "this", "that", "to", "up", "via", 130 | "with"]; 131 | r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi'); 132 | s = s.replace(r, ''); 133 | // if downcode doesn't hit, the char will be stripped here 134 | s = s.replace(/[^-\w\s]/g, ''); // remove unneeded chars 135 | s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces 136 | s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens 137 | s = s.toLowerCase(); // convert to lowercase 138 | return s.substring(0, num_chars);// trim to first num_chars chars 139 | } 140 | 141 | -------------------------------------------------------------------------------- /examples/django/1.4/README: -------------------------------------------------------------------------------- 1 | django admin的用户名:root,密码:root 2 | -------------------------------------------------------------------------------- /examples/django/1.4/config.yaml: -------------------------------------------------------------------------------- 1 | name: pylabs 2 | version: 9 3 | 4 | libraries: 5 | - name: "django" 6 | version: "1.4" 7 | 8 | handlers: 9 | - url: /foo 10 | static_dir: foo 11 | -------------------------------------------------------------------------------- /examples/django/1.4/index.wsgi: -------------------------------------------------------------------------------- 1 | import sae 2 | from mysite import wsgi 3 | 4 | application = sae.create_wsgi_app(wsgi.application) 5 | -------------------------------------------------------------------------------- /examples/django/1.4/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /examples/django/1.4/mysite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.4/mysite/__init__.py -------------------------------------------------------------------------------- /examples/django/1.4/mysite/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for mysite project. 2 | 3 | DEBUG = True 4 | TEMPLATE_DEBUG = DEBUG 5 | 6 | ADMINS = ( 7 | # ('Your Name', 'your_email@example.com'), 8 | ) 9 | 10 | MANAGERS = ADMINS 11 | 12 | import os 13 | 14 | if 'SERVER_SOFTWARE' in os.environ: 15 | from sae.const import ( 16 | MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB 17 | ) 18 | else: 19 | # Make `python manage.py syncdb` works happy! 20 | MYSQL_HOST = 'localhost' 21 | MYSQL_PORT = '3306' 22 | MYSQL_USER = 'root' 23 | MYSQL_PASS = 'root' 24 | MYSQL_DB = 'app_pylabs' 25 | 26 | DATABASES = { 27 | 'default': { 28 | 'ENGINE': 'django.db.backends.mysql', 29 | 'NAME': MYSQL_DB, 30 | 'USER': MYSQL_USER, 31 | 'PASSWORD': MYSQL_PASS, 32 | 'HOST': MYSQL_HOST, 33 | 'PORT': MYSQL_PORT, 34 | } 35 | } 36 | 37 | # Local time zone for this installation. Choices can be found here: 38 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 39 | # although not all choices may be available on all operating systems. 40 | # On Unix systems, a value of None will cause Django to use the same 41 | # timezone as the operating system. 42 | # If running in a Windows environment this must be set to the same as your 43 | # system time zone. 44 | TIME_ZONE = 'America/Chicago' 45 | 46 | # Language code for this installation. All choices can be found here: 47 | # http://www.i18nguy.com/unicode/language-identifiers.html 48 | LANGUAGE_CODE = 'en-us' 49 | 50 | SITE_ID = 1 51 | 52 | # If you set this to False, Django will make some optimizations so as not 53 | # to load the internationalization machinery. 54 | USE_I18N = True 55 | 56 | # If you set this to False, Django will not format dates, numbers and 57 | # calendars according to the current locale. 58 | USE_L10N = True 59 | 60 | # If you set this to False, Django will not use timezone-aware datetimes. 61 | USE_TZ = True 62 | 63 | # Absolute filesystem path to the directory that will hold user-uploaded files. 64 | # Example: "/home/media/media.lawrence.com/media/" 65 | MEDIA_ROOT = '' 66 | 67 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 68 | # trailing slash. 69 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 70 | MEDIA_URL = '' 71 | 72 | # Absolute path to the directory static files should be collected to. 73 | # Don't put anything in this directory yourself; store your static files 74 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 75 | # Example: "/home/media/media.lawrence.com/static/" 76 | STATIC_ROOT = '' 77 | 78 | # URL prefix for static files. 79 | # Example: "http://media.lawrence.com/static/" 80 | STATIC_URL = '/static/' 81 | 82 | # Additional locations of static files 83 | STATICFILES_DIRS = ( 84 | # Put strings here, like "/home/html/static" or "C:/www/django/static". 85 | # Always use forward slashes, even on Windows. 86 | # Don't forget to use absolute paths, not relative paths. 87 | ) 88 | 89 | # List of finder classes that know how to find static files in 90 | # various locations. 91 | STATICFILES_FINDERS = ( 92 | 'django.contrib.staticfiles.finders.FileSystemFinder', 93 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 94 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 95 | ) 96 | 97 | # Make this unique, and don't share it with anybody. 98 | SECRET_KEY = 'up)24f2-l-+#g7ek4hp8ri1ng$@nbwqk+(fhdshgn9sc#b*oyl' 99 | 100 | # List of callables that know how to import templates from various sources. 101 | TEMPLATE_LOADERS = ( 102 | 'django.template.loaders.filesystem.Loader', 103 | 'django.template.loaders.app_directories.Loader', 104 | # 'django.template.loaders.eggs.Loader', 105 | ) 106 | 107 | MIDDLEWARE_CLASSES = ( 108 | 'django.middleware.common.CommonMiddleware', 109 | 'django.contrib.sessions.middleware.SessionMiddleware', 110 | 'django.middleware.csrf.CsrfViewMiddleware', 111 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 112 | 'django.contrib.messages.middleware.MessageMiddleware', 113 | # Uncomment the next line for simple clickjacking protection: 114 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 115 | ) 116 | 117 | ROOT_URLCONF = 'mysite.urls' 118 | 119 | # Python dotted path to the WSGI application used by Django's runserver. 120 | WSGI_APPLICATION = 'mysite.wsgi.application' 121 | 122 | import os.path 123 | 124 | TEMPLATE_DIRS = ( 125 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 126 | # Always use forward slashes, even on Windows. 127 | # Don't forget to use absolute paths, not relative paths. 128 | os.path.join(os.path.dirname(__file__), 'templates'), 129 | ) 130 | 131 | INSTALLED_APPS = ( 132 | 'django.contrib.auth', 133 | 'django.contrib.contenttypes', 134 | 'django.contrib.sessions', 135 | 'django.contrib.sites', 136 | 'django.contrib.messages', 137 | 'django.contrib.staticfiles', 138 | # Uncomment the next line to enable the admin: 139 | 'django.contrib.admin', 140 | # Uncomment the next line to enable admin documentation: 141 | # 'django.contrib.admindocs', 142 | 'polls', 143 | ) 144 | 145 | # A sample logging configuration. The only tangible logging 146 | # performed by this configuration is to send an email to 147 | # the site admins on every HTTP 500 error when DEBUG=False. 148 | # See http://docs.djangoproject.com/en/dev/topics/logging for 149 | # more details on how to customize your logging configuration. 150 | LOGGING = { 151 | 'version': 1, 152 | 'disable_existing_loggers': False, 153 | 'filters': { 154 | 'require_debug_false': { 155 | '()': 'django.utils.log.RequireDebugFalse' 156 | } 157 | }, 158 | 'handlers': { 159 | 'mail_admins': { 160 | 'level': 'ERROR', 161 | 'filters': ['require_debug_false'], 162 | 'class': 'django.utils.log.AdminEmailHandler' 163 | } 164 | }, 165 | 'loggers': { 166 | 'django.request': { 167 | 'handlers': ['mail_admins'], 168 | 'level': 'ERROR', 169 | 'propagate': True, 170 | }, 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /examples/django/1.4/mysite/templates/404.html: -------------------------------------------------------------------------------- 1 |

    NOT FOUND

    2 | -------------------------------------------------------------------------------- /examples/django/1.4/mysite/templates/500.html: -------------------------------------------------------------------------------- 1 |

    INTERNAL ERROR

    2 | -------------------------------------------------------------------------------- /examples/django/1.4/mysite/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | # Uncomment the next two lines to enable the admin: 4 | from django.contrib import admin 5 | admin.autodiscover() 6 | 7 | urlpatterns = patterns('', 8 | # Examples: 9 | # url(r'^$', 'mysite.views.home', name='home'), 10 | # url(r'^mysite/', include('mysite.foo.urls')), 11 | url(r'^polls/', include('polls.urls')), 12 | 13 | # Uncomment the admin/doc line below to enable admin documentation: 14 | # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 15 | 16 | # Uncomment the next line to enable the admin: 17 | url(r'^admin/', include(admin.site.urls)), 18 | ) 19 | 20 | # Serve static files for admin, use this for debug usage only 21 | # `python manage.py collectstatic` is preferred. 22 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns 23 | urlpatterns += staticfiles_urlpatterns() 24 | 25 | -------------------------------------------------------------------------------- /examples/django/1.4/mysite/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for mysite project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 19 | 20 | # This application object is used by any WSGI server configured to use this 21 | # file. This includes Django's development server, if the WSGI_APPLICATION 22 | # setting points here. 23 | from django.core.wsgi import get_wsgi_application 24 | application = get_wsgi_application() 25 | 26 | # Apply WSGI middleware here. 27 | # from helloworld.wsgi import HelloWorldApplication 28 | # application = HelloWorldApplication(application) 29 | -------------------------------------------------------------------------------- /examples/django/1.4/polls/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/django/1.4/polls/__init__.py -------------------------------------------------------------------------------- /examples/django/1.4/polls/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from polls.models import Poll, Choice 3 | 4 | class ChoiceInline(admin.StackedInline): 5 | model = Choice 6 | extra = 3 7 | 8 | class PollAdmin(admin.ModelAdmin): 9 | fieldsets = [ 10 | (None, {'fields': ['question']}), 11 | ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), 12 | ] 13 | inlines = [ChoiceInline] 14 | list_display = ('question', 'pub_date', 'was_published_recently') 15 | list_filter = ['pub_date'] 16 | search_fields = ['question'] 17 | date_hierarchy = 'pub_date' 18 | 19 | admin.site.register(Poll, PollAdmin) 20 | -------------------------------------------------------------------------------- /examples/django/1.4/polls/models.py: -------------------------------------------------------------------------------- 1 | import time 2 | from django.db import models 3 | 4 | class Poll(models.Model): 5 | question = models.CharField(max_length=200) 6 | pub_date = models.DateTimeField('date published') 7 | 8 | def was_published_recently(self): 9 | return self.pub_date >= time.now() - datetime.timedelta(days=1) 10 | 11 | was_published_recently.admin_order_field = 'pub_date' 12 | was_published_recently.boolean = True 13 | was_published_recently.short_description = 'Published recently?' 14 | 15 | class Choice(models.Model): 16 | poll = models.ForeignKey(Poll) 17 | choice = models.CharField(max_length=200) 18 | votes = models.IntegerField() 19 | -------------------------------------------------------------------------------- /examples/django/1.4/polls/templates/detail.html: -------------------------------------------------------------------------------- 1 |

    {{ poll.question }}

    2 | 3 | {% if error_message %}

    {{ error_message }}

    {% endif %} 4 | 5 |
    6 | {% csrf_token %} 7 | {% for choice in poll.choice_set.all %} 8 | 9 |
    10 | {% endfor %} 11 | 12 |
    13 | -------------------------------------------------------------------------------- /examples/django/1.4/polls/templates/index.html: -------------------------------------------------------------------------------- 1 | {% if latest_poll_list %} 2 | 7 | {% else %} 8 |

    No polls are available.

    9 | {% endif %} 10 | -------------------------------------------------------------------------------- /examples/django/1.4/polls/templates/results.html: -------------------------------------------------------------------------------- 1 |

    {{ poll.question }}

    2 | 3 |
      4 | {% for choice in poll.choice_set.all %} 5 |
    • {{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
    • 6 | {% endfor %} 7 |
    8 | 9 | Vote again? 10 | -------------------------------------------------------------------------------- /examples/django/1.4/polls/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /examples/django/1.4/polls/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | urlpatterns = patterns('polls.views', 4 | url(r'^$', 'index'), 5 | url(r'^(?P\d+)/$', 'detail'), 6 | url(r'^(?P\d+)/results/$', 'results'), 7 | url(r'^(?P\d+)/vote/$', 'vote'), 8 | ) 9 | -------------------------------------------------------------------------------- /examples/django/1.4/polls/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render_to_response, get_object_or_404 2 | from django.template import RequestContext 3 | from django.http import HttpResponseRedirect, HttpResponse 4 | from django.core.urlresolvers import reverse 5 | from polls.models import Poll, Choice 6 | 7 | def index(request): 8 | latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5] 9 | return render_to_response('index.html', {'latest_poll_list': latest_poll_list}) 10 | 11 | def detail(request, poll_id): 12 | p = get_object_or_404(Poll, pk=poll_id) 13 | return render_to_response('detail.html', {'poll': p}, 14 | context_instance=RequestContext(request)) 15 | 16 | def vote(request, poll_id): 17 | p = get_object_or_404(Poll, pk=poll_id) 18 | try: 19 | selected_choice = p.choice_set.get(pk=request.POST['choice']) 20 | except (KeyError, Choice.DoesNotExist): 21 | # Redisplay the poll voting form. 22 | return render_to_response('detail.html', { 23 | 'poll': p, 24 | 'error_message': "You didn't select a choice.", 25 | }, context_instance=RequestContext(request)) 26 | else: 27 | selected_choice.votes += 1 28 | selected_choice.save() 29 | # Always return an HttpResponseRedirect after successfully dealing 30 | # with POST data. This prevents data from being posted twice if a 31 | # user hits the Back button. 32 | return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,))) 33 | 34 | def results(request, poll_id): 35 | p = get_object_or_404(Poll, pk=poll_id) 36 | return render_to_response('results.html', {'poll': p}) 37 | -------------------------------------------------------------------------------- /examples/flask/README: -------------------------------------------------------------------------------- 1 | Hello, Flask! 2 | -------------------------------------------------------------------------------- /examples/flask/app.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/flask/app.py -------------------------------------------------------------------------------- /examples/flask/index.wsgi: -------------------------------------------------------------------------------- 1 | 2 | import sae 3 | 4 | from myapp import app 5 | 6 | application = sae.create_wsgi_app(app) 7 | -------------------------------------------------------------------------------- /examples/flask/myapp.py: -------------------------------------------------------------------------------- 1 | 2 | import MySQLdb 3 | from flask import Flask, g, request 4 | 5 | app = Flask(__name__) 6 | app.debug = True 7 | 8 | from sae.const import (MYSQL_HOST, MYSQL_HOST_S, 9 | MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB 10 | ) 11 | 12 | @app.before_request 13 | def before_request(): 14 | g.db = MySQLdb.connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, 15 | MYSQL_DB, port=int(MYSQL_PORT)) 16 | 17 | @app.teardown_request 18 | def teardown_request(exception): 19 | if hasattr(g, 'db'): g.db.close() 20 | 21 | @app.route('/') 22 | def hello(): 23 | return "Hello, world! - Flask" 24 | 25 | @app.route('/demo', methods=['GET', 'POST']) 26 | def greeting(): 27 | html = '' 28 | 29 | if request.method == 'POST': 30 | c = g.db.cursor() 31 | c.execute("insert into demo(text) values(%s)", (request.form['text'])) 32 | 33 | html += """ 34 |
    35 |
    36 |
    37 |
    38 | """ 39 | c = g.db.cursor() 40 | c.execute('select * from demo') 41 | msgs = list(c.fetchall()) 42 | msgs.reverse() 43 | for row in msgs: 44 | html += '

    ' + row[-1] + '

    ' 45 | 46 | return html 47 | 48 | -------------------------------------------------------------------------------- /examples/helloworld/1/index.wsgi: -------------------------------------------------------------------------------- 1 | import sae 2 | 3 | def app(environ, start_response): 4 | status = '200 OK' 5 | response_headers = [('Content-type', 'text/plain')] 6 | start_response(status, response_headers) 7 | return ['Hello, world!'] 8 | 9 | application = sae.create_wsgi_app(app) 10 | -------------------------------------------------------------------------------- /examples/matplotshell/README: -------------------------------------------------------------------------------- 1 | Run matplotlib source code on SAE. 2 | 3 | http://matplotlib.org/gallery.html 4 | -------------------------------------------------------------------------------- /examples/matplotshell/config.yaml: -------------------------------------------------------------------------------- 1 | name: matplotshell 2 | version: 1 3 | 4 | libraries: 5 | - name: numpy 6 | version: "1.6.1" 7 | 8 | - name: matplotlib 9 | version: "1.1.1" 10 | -------------------------------------------------------------------------------- /examples/matplotshell/index.wsgi: -------------------------------------------------------------------------------- 1 | import sae 2 | import cStringIO 3 | from bottle import Bottle, request, response 4 | 5 | app = Bottle() 6 | 7 | @app.route('/') 8 | def index(): 9 | return """ 10 | 11 | 12 | 13 | matplotlib shell 14 | 15 | 16 |
    17 |
    18 | 19 |
    20 | 21 |
    22 |
    23 |
    24 |
    25 |
    26 | 27 | 28 | """ 29 | 30 | @app.route('/plot', method='POST') 31 | def plot(): 32 | code = request.forms['code'] 33 | 34 | import matplotlib.pyplot as plt 35 | #import pylab as plt 36 | __f__ = cStringIO.StringIO() 37 | 38 | dct = dict() 39 | dct['__name__'] = '__main__' 40 | exec code in dct 41 | plt.savefig(__f__) 42 | data = __f__.getvalue() 43 | __f__.close() 44 | 45 | # close current session 46 | plt.close() 47 | 48 | response.content_type = 'image/png' 49 | return data 50 | 51 | import bottle 52 | bottle.debug(True) 53 | 54 | application = sae.create_wsgi_app(app) 55 | -------------------------------------------------------------------------------- /examples/renren/config.yaml: -------------------------------------------------------------------------------- 1 | name: pylabs 2 | version: 2 3 | -------------------------------------------------------------------------------- /examples/renren/db.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 3.3.8.1 3 | -- http://www.phpmyadmin.net 4 | -- 5 | -- 主机: w.rdc.sae.sina.com.cn:3307 6 | -- 生成日期: 2012 年 03 月 14 日 00:24 7 | -- 服务器版本: 5.1.47 8 | -- PHP 版本: 5.2.9 9 | 10 | SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; 11 | 12 | -- 13 | -- 数据库: `app_pylabs` 14 | -- 15 | 16 | -- -------------------------------------------------------- 17 | 18 | -- 19 | -- 表的结构 `users` 20 | -- 21 | 22 | CREATE TABLE IF NOT EXISTS `users` ( 23 | `uid` int(11) NOT NULL, 24 | `name` varchar(128) NOT NULL, 25 | `avatar` varchar(128) NOT NULL, 26 | `access_token` varchar(256) NOT NULL, 27 | UNIQUE KEY `uid` (`uid`) 28 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 29 | 30 | -------------------------------------------------------------------------------- /examples/renren/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RenRen OAuth Example 6 | 7 | 8 |

    {{ error.error }}

    9 |

    {{ error.error_description }}

    10 |

    For more information please browse the error page.

    11 |

    Log in with RenRen

    12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/renren/index.wsgi: -------------------------------------------------------------------------------- 1 | import sae 2 | from renrenoauth import app 3 | 4 | application = sae.create_wsgi_app(app) 5 | -------------------------------------------------------------------------------- /examples/renren/oauth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RenRen OAuth Example 6 | 7 | 8 | {% if current_user %} 9 |

    10 | 首页 11 |

    12 |

    13 | You are logged in as {{ current_user.name }} 14 |

    15 |

    16 | Log out 17 |

    18 | {% else %} 19 |

    20 | You are not yet logged into this site 21 |

    22 |

    23 | Log in with RenRen 24 |

    25 | {% end %} 26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/segment/config.yaml: -------------------------------------------------------------------------------- 1 | name: pylabs 2 | version: 3 3 | -------------------------------------------------------------------------------- /examples/segment/index.wsgi: -------------------------------------------------------------------------------- 1 | #-*-coding: utf8 -*- 2 | 3 | """ 4 | 分词服务请求 5 | ------------ 6 | 7 | SAE分词服务请求采用以下形式的HTTP网址: :: 8 | 9 | http://segment.sae.sina.com.cn/urlclient.php?parameters 10 | 11 | parameters为请求参数,多个参数之间使用&分割,以下列出了这些参数和其可能的值。 12 | 13 | * word_tag: 是否返回词性数据。0表示不返回,1表示返回,默认为0不返回。 14 | * encoding: 请求分词的文本的编码,可以为: GB18030、UTF-8、UCS-2,默认为UTF-8。 15 | 16 | 请求分词的文本以post的形式提交。 17 | 18 | * context: 请求分词的文本。目前限制文本大小最大为10KB。 19 | 20 | 分词服务响应 21 | ------------ 22 | 23 | 分词服务的响应数据为json格式,格式如下: :: 24 | 25 | [ 26 | {"word":"采莲","word_tag":"171","index":"1"}, 27 | {"word":"赋","word_tag":"170","index":"2"} 28 | ] 29 | 30 | 响应数据为一个list,list中每个元素为一个dict,每个dict中包含以下数据: 31 | 32 | * index: 序列号,按在请求文本中的位置依次递增。 33 | * word: 单词 34 | * word_tag: 单词的词性,仅当输入parameters里word_tag为1时包含该项。 35 | 36 | 词性代码: :: 37 | 38 | 0 POSTAG_ID_UNKNOW 未知 39 | 10 POSTAG_ID_A 形容词 40 | 20 POSTAG_ID_B 区别词 41 | 30 POSTAG_ID_C 连词 42 | 31 POSTAG_ID_C_N 体词连接 43 | 32 POSTAG_ID_C_Z 分句连接 44 | 40 POSTAG_ID_D 副词 45 | 41 POSTAG_ID_D_B 副词("不") 46 | 42 POSTAG_ID_D_M 副词("没") 47 | 50 POSTAG_ID_E 叹词 48 | 60 POSTAG_ID_F 方位词 49 | 61 POSTAG_ID_F_S 方位短语(处所词+方位词) 50 | 62 POSTAG_ID_F_N 方位短语(名词+方位词“地上”) 51 | 63 POSTAG_ID_F_V 方位短语(动词+方位词“取前”) 52 | 64 POSTAG_ID_F_Z 方位短语(动词+方位词“取前”) 53 | 70 POSTAG_ID_H 前接成分 54 | 71 POSTAG_ID_H_M 数词前缀(“数”---数十) 55 | 72 POSTAG_ID_H_T 时间词前缀(“公元”“明永乐”) 56 | 73 POSTAG_ID_H_NR 姓氏 57 | 74 POSTAG_ID_H_N 姓氏 58 | 80 POSTAG_ID_K 后接成分 59 | 81 POSTAG_ID_K_M 数词后缀(“来”--,十来个) 60 | 82 POSTAG_ID_K_T 时间词后缀(“初”“末”“时”) 61 | 83 POSTAG_ID_K_N 名词后缀(“们”) 62 | 84 POSTAG_ID_K_S 处所词后缀(“苑”“里”) 63 | 85 POSTAG_ID_K_Z 状态词后缀(“然”) 64 | 86 POSTAG_ID_K_NT 状态词后缀(“然”) 65 | 87 POSTAG_ID_K_NS 状态词后缀(“然”) 66 | 90 POSTAG_ID_M 数词 67 | 95 POSTAG_ID_N 名词 68 | 96 POSTAG_ID_N_RZ 人名(“毛泽东”) 69 | 97 POSTAG_ID_N_T 机构团体(“团”的声母为t,名词代码n和t并在一起。“公司”) 70 | 98 POSTAG_ID_N_TA .... 71 | 99 POSTAG_ID_N_TZ 机构团体名("北大") 72 | 100 POSTAG_ID_N_Z 其他专名(“专”的声母的第1个字母为z,名词代码n和z并在一起。) 73 | 101 POSTAG_ID_NS 名处词 74 | 102 POSTAG_ID_NS_Z 地名(名处词专指:“中国”) 75 | 103 POSTAG_ID_N_M n-m,数词开头的名词(三个学生) 76 | 104 POSTAG_ID_N_RB n-rb,以区别词/代词开头的名词(该学校,该生) 77 | 107 POSTAG_ID_O 拟声词 78 | 108 POSTAG_ID_P 介词 79 | 110 POSTAG_ID_Q 量词 80 | 111 POSTAG_ID_Q_V 动量词(“趟”“遍”) 81 | 112 POSTAG_ID_Q_T 时间量词(“年”“月”“期”) 82 | 113 POSTAG_ID_Q_H 货币量词(“元”“美元”“英镑”) 83 | 120 POSTAG_ID_R 代词 84 | 121 POSTAG_ID_R_D 副词性代词(“怎么”) 85 | 122 POSTAG_ID_R_M 数词性代词(“多少”) 86 | 123 POSTAG_ID_R_N 名词性代词(“什么”“谁”) 87 | 124 POSTAG_ID_R_S 处所词性代词(“哪儿”) 88 | 125 POSTAG_ID_R_T 时间词性代词(“何时”) 89 | 126 POSTAG_ID_R_Z 谓词性代词(“怎么样”) 90 | 127 POSTAG_ID_R_B 区别词性代词(“某”“每”) 91 | 130 POSTAG_ID_S 处所词(取英语space的第1个字母。“东部”) 92 | 131 POSTAG_ID_S_Z 处所词(取英语space的第1个字母。“东部”) 93 | 132 POSTAG_ID_T 时间词(取英语time的第1个字母) 94 | 133 POSTAG_ID_T_Z 时间专指(“唐代”“西周”) 95 | 140 POSTAG_ID_U 助词 96 | 141 POSTAG_ID_U_N 定语助词(“的”) 97 | 142 POSTAG_ID_U_D 状语助词(“地”) 98 | 143 POSTAG_ID_U_C 补语助词(“得”) 99 | 144 POSTAG_ID_U_Z 谓词后助词(“了、着、过”) 100 | 145 POSTAG_ID_U_S 体词后助词(“等、等等”) 101 | 146 POSTAG_ID_U_SO 助词(“所”) 102 | 150 POSTAG_ID_W 标点符号 103 | 151 POSTAG_ID_W_D 顿号(“、”) 104 | 152 POSTAG_ID_W_SP 句号(“。”) 105 | 153 POSTAG_ID_W_S 分句尾标点(“,”“;”) 106 | 154 POSTAG_ID_W_L 搭配型标点左部 107 | 155 POSTAG_ID_W_R 搭配型标点右部(“》”“]”“)”) 108 | 156 POSTAG_ID_W_H 中缀型符号 109 | 160 POSTAG_ID_Y 语气词(取汉字“语”的声母。“吗”“吧”“啦”) 110 | 170 POSTAG_ID_V 及物动词(取英语动词verb的第一个字母。) 111 | 171 POSTAG_ID_V_O 不及物谓词(谓宾结构“剃头”) 112 | 172 POSTAG_ID_V_E 动补结构动词(“取出”“放到”) 113 | 173 POSTAG_ID_V_SH 动词“是” 114 | 174 POSTAG_ID_V_YO 动词“有” 115 | 116 | 175 POSTAG_ID_V_Q 趋向动词(“来”“去”“进来”) 117 | 176 POSTAG_ID_V_A 助动词(“应该”“能够”) 118 | 180 POSTAG_ID_Z 状态词(不及物动词,v-o、sp之外的不及物动词) 119 | 190 POSTAG_ID_X 语素字 120 | 191 POSTAG_ID_X_N 名词语素(“琥”) 121 | 192 POSTAG_ID_X_V 动词语素(“酹”) 122 | 193 POSTAG_ID_X_S 处所词语素(“中”“日”“美”) 123 | 194 POSTAG_ID_X_T 时间词语素(“唐”“宋”“元”) 124 | 195 POSTAG_ID_X_Z 状态词语素(“伟”“芳”) 125 | 196 POSTAG_ID_X_B 状态词语素(“伟”“芳”) 126 | 200 POSTAG_ID_SP 不及物谓词(主谓结构“腰酸”“头疼”) 127 | 201 POSTAG_ID_MQ 数量短语(“叁个”) 128 | 202 POSTAG_ID_RQ 代量短语(“这个”) 129 | 210 POSTAG_ID_AD 副形词(直接作状语的形容词) 130 | 211 POSTAG_ID_AN 名形词(具有名词功能的形容词) 131 | 212 POSTAG_ID_VD 副动词(直接作状语的动词) 132 | 213 POSTAG_ID_VN 名动词(指具有名词功能的动词) 133 | 230 POSTAG_ID_SPACE 空格 134 | """ 135 | 136 | import sae 137 | import urllib 138 | import urllib2 139 | 140 | _SEGMENT_BASE_URL = 'http://segment.sae.sina.com.cn/urlclient.php' 141 | 142 | some_chinese_text = """ 143 | 采莲赋 (萧绎 ) 144 | 紫茎兮文波,红莲兮芰荷。绿房兮翠盖,素实兮黄螺。于时妖童媛女,荡舟心许, 145 | (益鸟,音益)首徐回,兼传羽杯。棹将移而藻挂,船欲动而萍开。尔其纤腰束 146 | 素,迁延顾步。夏始春余,叶嫩花初。恐沾裳而浅笑,畏倾船而敛裾,故以水溅 147 | 兰桡,芦侵罗(衤荐,音间)。菊泽未反,梧台迥见,荇湿沾衫,菱长绕钏。泛 148 | 柏舟而容与,歌采莲于江渚。歌曰:“碧玉小家女,来嫁汝南王。莲花乱脸色, 149 | 荷叶杂衣香。因持荐君子,愿袭芙蓉裳。” 150 | """ 151 | 152 | def segment(text): 153 | payload = urllib.urlencode([('context', text),]) 154 | args = urllib.urlencode([('word_tag', 1), ('encoding', 'UTF-8'),]) 155 | url = _SEGMENT_BASE_URL + '?' + args 156 | return urllib2.urlopen(url, payload).read() 157 | 158 | def app(environ, start_response): 159 | status = '200 OK' 160 | response_headers = [('Content-type', 'text/plain')] 161 | start_response(status, response_headers) 162 | 163 | output = segment(some_chinese_text) 164 | 165 | return [output] 166 | 167 | application = sae.create_wsgi_app(app) 168 | -------------------------------------------------------------------------------- /examples/static-site/README: -------------------------------------------------------------------------------- 1 | A demo for pure static site with 0 python code. 2 | -------------------------------------------------------------------------------- /examples/static-site/config.yaml: -------------------------------------------------------------------------------- 1 | name: staticsite 2 | version: 1 3 | 4 | handlers: 5 | - url: / 6 | static_path: www 7 | -------------------------------------------------------------------------------- /examples/static-site/www/appengine_button_noborder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinacloud/sae-python-dev-guide/d60f361535ead97156ad3b3244e03712e9a3a9eb/examples/static-site/www/appengine_button_noborder.gif -------------------------------------------------------------------------------- /examples/static-site/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello App Engine! 4 | 5 | 6 | 7 | Hello Sina App Engine! 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/tornado/async/config.yaml: -------------------------------------------------------------------------------- 1 | name: pylabs 2 | version: 1 3 | 4 | worker: tornado 5 | -------------------------------------------------------------------------------- /examples/tornado/async/index.wsgi: -------------------------------------------------------------------------------- 1 | import tornado.web 2 | from tornado.httpclient import AsyncHTTPClient 3 | 4 | class MainHandler(tornado.web.RequestHandler): 5 | @tornado.web.asynchronous 6 | def get(self): 7 | http = tornado.httpclient.AsyncHTTPClient() 8 | http.fetch("http://wiki.westeros.org", callback=self._callback) 9 | self.write("Hello to the Tornado world! ") 10 | self.flush() 11 | 12 | def _callback(self, response): 13 | self.write(response.body) 14 | self.finish() 15 | 16 | settings = { 17 | "debug": True, 18 | } 19 | 20 | # application should be an instance of `tornado.web.Application`, 21 | # and don't wrap it with `sae.create_wsgi_app` 22 | application = tornado.web.Application([ 23 | (r"/", MainHandler), 24 | ], **settings) 25 | -------------------------------------------------------------------------------- /examples/tornado/wsgi/README: -------------------------------------------------------------------------------- 1 | Hello, Tornado! 2 | -------------------------------------------------------------------------------- /examples/tornado/wsgi/index.wsgi: -------------------------------------------------------------------------------- 1 | import tornado.wsgi 2 | 3 | import sae 4 | 5 | class MainHandler(tornado.web.RequestHandler): 6 | def get(self): 7 | self.write("Hello, world! - Tornado") 8 | 9 | app = tornado.wsgi.WSGIApplication([ 10 | (r"/", MainHandler), 11 | ]) 12 | 13 | application = sae.create_wsgi_app(app) 14 | -------------------------------------------------------------------------------- /examples/trac/README.md: -------------------------------------------------------------------------------- 1 | 如何在SAE上安装和运行Trac 2 | --------------------------- 3 | 4 | 1. 下载本示例代码,进入本代码所在目录,使用以下命令打包安装所有的依赖包 5 | 6 | saecloud install -r requirements.txt 7 | 8 | 该命令会下载 `Trac-1.0.1` 并安装到应用的 `site-packages` 目录下。 9 | 10 | 2. 修改 `project/conf/trac.ini` 中的mysql配置为你的应用的配置。 11 | 12 | [trac] 13 | ... 14 | database=mysql://$accesskey:$secretkey@w.rdc.sae.sina.com.cn:3307/app_$appname 15 | 16 | 3. 修改 `config.yaml` 中的应用名为你的应用名,部署应用。 17 | 18 | saecloud deploy 19 | 20 | 4. 进入应用的MYSQL管理页面,导入 `setup.sql` 。 21 | 22 | 4. 打开 http://$appname.sinaapp.com 即可看到示例trac的页面了。 23 | 24 | 5. 删除 `project` 目录,使用 `trac-admin` 创建你自己的项目,根据需要修改 `index.wsgi` 25 | 中的 `TRAC_ENV` 环境变量即可。 26 | 27 | 示例页面: http://tractest.sinaapp.com 28 | -------------------------------------------------------------------------------- /examples/trac/config.yaml: -------------------------------------------------------------------------------- 1 | name: tractest 2 | version: 1 3 | -------------------------------------------------------------------------------- /examples/trac/index.wsgi: -------------------------------------------------------------------------------- 1 | #-*-coding: utf8 -*- 2 | 3 | import os.path 4 | root = os.path.dirname(__file__) 5 | 6 | import sys 7 | sys.path.insert(0, os.path.join(root, 'site-packages')) 8 | 9 | os.environ['TRAC_ENV'] = os.path.join(root, 'project') 10 | #os.environ['TRAC_ENV_PARENT_DIR'] = os.path.join(root, 'projects') 11 | 12 | import trac.web.main as main 13 | application = main.dispatch_request 14 | -------------------------------------------------------------------------------- /examples/trac/project/README: -------------------------------------------------------------------------------- 1 | This directory contains a Trac environment. 2 | Visit http://trac.edgewall.org/ for more information. 3 | -------------------------------------------------------------------------------- /examples/trac/project/VERSION: -------------------------------------------------------------------------------- 1 | Trac Environment Version 1 2 | -------------------------------------------------------------------------------- /examples/trac/project/conf/trac.ini: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | [attachment] 4 | max_size = 262144 5 | max_zip_size = 2097152 6 | render_unsafe_content = false 7 | 8 | [browser] 9 | color_scale = True 10 | downloadable_paths = /trunk, /branches/*, /tags/* 11 | hide_properties = svk:merge 12 | intermediate_color = 13 | intermediate_point = 14 | newest_color = (255, 136, 136) 15 | oldest_color = (136, 136, 255) 16 | oneliner_properties = trac:summary 17 | render_unsafe_content = false 18 | wiki_properties = trac:description 19 | 20 | [changeset] 21 | max_diff_bytes = 10000000 22 | max_diff_files = 0 23 | wiki_format_messages = true 24 | 25 | [header_logo] 26 | alt = (please configure the [header_logo] section in trac.ini) 27 | height = -1 28 | link = 29 | src = site/your_project_logo.png 30 | width = -1 31 | 32 | [inherit] 33 | htdocs_dir = 34 | plugins_dir = 35 | templates_dir = 36 | 37 | [logging] 38 | log_file = trac.log 39 | # log_format = 40 | log_level = DEBUG 41 | log_type = none 42 | 43 | [milestone] 44 | stats_provider = DefaultTicketGroupStatsProvider 45 | 46 | [mimeviewer] 47 | max_preview_size = 262144 48 | mime_map = text/x-dylan:dylan, text/x-idl:ice, text/x-ada:ads:adb 49 | mime_map_patterns = text/plain:README|INSTALL|COPYING.* 50 | pygments_default_style = trac 51 | pygments_modes = 52 | tab_width = 8 53 | treat_as_binary = application/octet-stream, application/pdf, application/postscript, application/msword,application/rtf, 54 | 55 | [notification] 56 | admit_domains = 57 | always_notify_owner = false 58 | always_notify_reporter = false 59 | always_notify_updater = true 60 | ambiguous_char_width = single 61 | batch_subject_template = $prefix Batch modify: $tickets_descr 62 | email_sender = SmtpEmailSender 63 | ignore_domains = 64 | mime_encoding = none 65 | sendmail_path = sendmail 66 | smtp_always_bcc = 67 | smtp_always_cc = 68 | smtp_default_domain = 69 | smtp_enabled = false 70 | smtp_from = trac@localhost 71 | smtp_from_author = false 72 | smtp_from_name = 73 | smtp_password = 74 | smtp_port = 25 75 | smtp_replyto = trac@localhost 76 | smtp_server = localhost 77 | smtp_subject_prefix = __default__ 78 | smtp_user = 79 | ticket_subject_template = $prefix #$ticket.id: $summary 80 | use_public_cc = false 81 | use_short_addr = false 82 | use_tls = false 83 | 84 | [project] 85 | admin = 86 | admin_trac_url = . 87 | descr = My example project 88 | footer = Visit the Trac open source project at
    http://trac.edgewall.org/ 89 | icon = common/trac.ico 90 | name = My Project 91 | url = 92 | 93 | [query] 94 | default_anonymous_query = status!=closed&cc~=$USER 95 | default_query = status!=closed&owner=$USER 96 | items_per_page = 100 97 | ticketlink_query = ?status=!closed 98 | 99 | [report] 100 | items_per_page = 100 101 | items_per_page_rss = 0 102 | 103 | [revisionlog] 104 | default_log_limit = 100 105 | graph_colors = ['#cc0', '#0c0', '#0cc', '#00c', '#c0c', '#c00'] 106 | 107 | [roadmap] 108 | stats_provider = DefaultTicketGroupStatsProvider 109 | 110 | [search] 111 | # default_disabled_filters = 112 | min_query_length = 3 113 | 114 | [sqlite] 115 | # extensions = 116 | 117 | [ticket] 118 | default_cc = 119 | default_component = 120 | default_description = 121 | default_keywords = 122 | default_milestone = 123 | default_owner = < default > 124 | default_priority = major 125 | default_resolution = fixed 126 | default_severity = 127 | default_summary = 128 | default_type = defect 129 | default_version = 130 | max_comment_size = 262144 131 | max_description_size = 262144 132 | preserve_newlines = default 133 | restrict_owner = false 134 | workflow = ConfigurableTicketWorkflow 135 | 136 | [ticket-workflow] 137 | accept = new,assigned,accepted,reopened -> accepted 138 | accept.operations = set_owner_to_self 139 | accept.permissions = TICKET_MODIFY 140 | leave = * -> * 141 | leave.default = 1 142 | leave.operations = leave_status 143 | reassign = new,assigned,accepted,reopened -> assigned 144 | reassign.operations = set_owner 145 | reassign.permissions = TICKET_MODIFY 146 | reopen = closed -> reopened 147 | reopen.operations = del_resolution 148 | reopen.permissions = TICKET_CREATE 149 | resolve = new,assigned,accepted,reopened -> closed 150 | resolve.operations = set_resolution 151 | resolve.permissions = TICKET_MODIFY 152 | 153 | [timeline] 154 | abbreviated_messages = True 155 | changeset_collapse_events = false 156 | changeset_long_messages = false 157 | changeset_show_files = 0 158 | default_daysback = 30 159 | max_daysback = 90 160 | newticket_formatter = oneliner 161 | ticket_show_details = false 162 | 163 | [trac] 164 | auth_cookie_lifetime = 0 165 | auth_cookie_path = 166 | authz_file = 167 | authz_module_name = 168 | auto_preview_timeout = 2.0 169 | auto_reload = False 170 | backup_dir = db 171 | base_url = 172 | check_auth_ip = false 173 | database = mysql://3mon0ozzn3:5hj2y31jm5xwj1ykh4kkmk2kyxm53h1klk1yxwwy@w.rdc.sae.sina.com.cn:3307/app_tractest 174 | debug_sql = False 175 | default_charset = utf-8 176 | default_dateinfo_format = relative 177 | genshi_cache_size = 128 178 | htdocs_location = 179 | ignore_auth_case = false 180 | jquery_location = 181 | jquery_ui_location = 182 | jquery_ui_theme_location = 183 | mainnav = wiki, timeline, roadmap, browser, tickets, newticket, search 184 | metanav = login, logout, prefs, help, about 185 | mysqldump_path = mysqldump 186 | never_obfuscate_mailto = false 187 | permission_policies = DefaultPermissionPolicy, LegacyAttachmentPolicy 188 | permission_store = DefaultPermissionStore 189 | pg_dump_path = pg_dump 190 | repository_dir = 191 | repository_sync_per_request = (default) 192 | repository_type = svn 193 | resizable_textareas = true 194 | secure_cookies = False 195 | show_email_addresses = false 196 | show_ip_addresses = false 197 | timeout = 20 198 | use_base_url_for_redirect = False 199 | 200 | [versioncontrol] 201 | allowed_repository_dir_prefixes = 202 | 203 | [wiki] 204 | ignore_missing_pages = false 205 | max_size = 262144 206 | render_unsafe_content = false 207 | safe_schemes = cvs, file, ftp, git, irc, http, https, news, sftp, smb, ssh, svn, svn+ssh 208 | split_page_names = false 209 | 210 | -------------------------------------------------------------------------------- /examples/trac/project/conf/trac.ini.sample: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | [attachment] 4 | max_size = 262144 5 | max_zip_size = 2097152 6 | render_unsafe_content = false 7 | 8 | [authz_policy] 9 | authz_file = 10 | 11 | [browser] 12 | color_scale = True 13 | downloadable_paths = /trunk, /branches/*, /tags/* 14 | hide_properties = svk:merge 15 | intermediate_color = 16 | intermediate_point = 17 | newest_color = (255, 136, 136) 18 | oldest_color = (136, 136, 255) 19 | oneliner_properties = trac:summary 20 | render_unsafe_content = false 21 | wiki_properties = trac:description 22 | 23 | [changeset] 24 | max_diff_bytes = 10000000 25 | max_diff_files = 0 26 | wiki_format_messages = true 27 | 28 | [git] 29 | cached_repository = false 30 | git_bin = git 31 | git_fs_encoding = utf-8 32 | persistent_cache = false 33 | # projects_base = 34 | # projects_list = 35 | # projects_url = 36 | shortrev_len = 7 37 | trac_user_rlookup = false 38 | use_committer_id = true 39 | use_committer_time = true 40 | wikishortrev_len = 40 41 | 42 | [header_logo] 43 | alt = (please configure the [header_logo] section in trac.ini) 44 | height = -1 45 | link = 46 | src = site/your_project_logo.png 47 | width = -1 48 | 49 | [inherit] 50 | htdocs_dir = 51 | plugins_dir = 52 | templates_dir = 53 | 54 | [logging] 55 | log_file = trac.log 56 | # log_format = 57 | log_level = DEBUG 58 | log_type = none 59 | 60 | [milestone] 61 | stats_provider = DefaultTicketGroupStatsProvider 62 | 63 | [mimeviewer] 64 | enscript_modes = text/x-dylan:dylan:4 65 | enscript_path = enscript 66 | max_preview_size = 262144 67 | mime_map = text/x-dylan:dylan, text/x-idl:ice, text/x-ada:ads:adb 68 | mime_map_patterns = text/plain:README|INSTALL|COPYING.* 69 | php_path = php 70 | pygments_default_style = trac 71 | pygments_modes = 72 | tab_width = 8 73 | treat_as_binary = application/octet-stream, application/pdf, application/postscript, application/msword,application/rtf, 74 | 75 | [notification] 76 | admit_domains = 77 | always_notify_owner = false 78 | always_notify_reporter = false 79 | always_notify_updater = true 80 | ambiguous_char_width = single 81 | batch_subject_template = $prefix Batch modify: $tickets_descr 82 | email_sender = SmtpEmailSender 83 | ignore_domains = 84 | mime_encoding = none 85 | sendmail_path = sendmail 86 | smtp_always_bcc = 87 | smtp_always_cc = 88 | smtp_default_domain = 89 | smtp_enabled = false 90 | smtp_from = trac@localhost 91 | smtp_from_author = false 92 | smtp_from_name = 93 | smtp_password = 94 | smtp_port = 25 95 | smtp_replyto = trac@localhost 96 | smtp_server = localhost 97 | smtp_subject_prefix = __default__ 98 | smtp_user = 99 | ticket_subject_template = $prefix #$ticket.id: $summary 100 | use_public_cc = false 101 | use_short_addr = false 102 | use_tls = false 103 | 104 | [project] 105 | admin = 106 | admin_trac_url = . 107 | descr = My example project 108 | footer = Visit the Trac open source project at
    http://trac.edgewall.org/ 109 | icon = common/trac.ico 110 | name = My Project 111 | url = 112 | 113 | [query] 114 | default_anonymous_query = status!=closed&cc~=$USER 115 | default_query = status!=closed&owner=$USER 116 | items_per_page = 100 117 | ticketlink_query = ?status=!closed 118 | 119 | [report] 120 | items_per_page = 100 121 | items_per_page_rss = 0 122 | 123 | [revisionlog] 124 | default_log_limit = 100 125 | graph_colors = ['#cc0', '#0c0', '#0cc', '#00c', '#c0c', '#c00'] 126 | 127 | [roadmap] 128 | stats_provider = DefaultTicketGroupStatsProvider 129 | 130 | [search] 131 | # default_disabled_filters = 132 | min_query_length = 3 133 | 134 | [sqlite] 135 | # extensions = 136 | 137 | [svn] 138 | branches = trunk, branches/* 139 | tags = tags/* 140 | 141 | [ticket] 142 | commit_ticket_update_check_perms = true 143 | commit_ticket_update_commands.close = close closed closes fix fixed fixes 144 | commit_ticket_update_commands.refs = addresses re references refs see 145 | commit_ticket_update_envelope = 146 | commit_ticket_update_notify = true 147 | default_cc = 148 | default_component = 149 | default_description = 150 | default_keywords = 151 | default_milestone = 152 | default_owner = < default > 153 | default_priority = major 154 | default_resolution = fixed 155 | default_severity = 156 | default_summary = 157 | default_type = defect 158 | default_version = 159 | max_comment_size = 262144 160 | max_description_size = 262144 161 | preserve_newlines = default 162 | restrict_owner = false 163 | workflow = ConfigurableTicketWorkflow 164 | 165 | [timeline] 166 | abbreviated_messages = True 167 | changeset_collapse_events = false 168 | changeset_long_messages = false 169 | changeset_show_files = 0 170 | default_daysback = 30 171 | max_daysback = 90 172 | newticket_formatter = oneliner 173 | ticket_show_details = false 174 | 175 | [trac] 176 | auth_cookie_lifetime = 0 177 | auth_cookie_path = 178 | authz_file = 179 | authz_module_name = 180 | auto_preview_timeout = 2.0 181 | auto_reload = False 182 | backup_dir = db 183 | base_url = 184 | check_auth_ip = false 185 | database = sqlite:db/trac.db 186 | debug_sql = False 187 | default_charset = utf-8 188 | default_dateinfo_format = relative 189 | genshi_cache_size = 128 190 | htdocs_location = 191 | ignore_auth_case = false 192 | jquery_location = 193 | jquery_ui_location = 194 | jquery_ui_theme_location = 195 | mainnav = wiki, timeline, roadmap, browser, tickets, newticket, search 196 | metanav = login, logout, prefs, help, about 197 | mysqldump_path = mysqldump 198 | never_obfuscate_mailto = false 199 | permission_policies = DefaultPermissionPolicy, LegacyAttachmentPolicy 200 | permission_store = DefaultPermissionStore 201 | pg_dump_path = pg_dump 202 | repository_dir = 203 | repository_sync_per_request = (default) 204 | repository_type = svn 205 | resizable_textareas = true 206 | secure_cookies = False 207 | show_email_addresses = false 208 | show_ip_addresses = false 209 | timeout = 20 210 | use_base_url_for_redirect = False 211 | 212 | [versioncontrol] 213 | allowed_repository_dir_prefixes = 214 | 215 | [wiki] 216 | ignore_missing_pages = false 217 | max_size = 262144 218 | render_unsafe_content = false 219 | safe_schemes = cvs, file, ftp, git, irc, http, https, news, sftp, smb, ssh, svn, svn+ssh 220 | split_page_names = false 221 | 222 | -------------------------------------------------------------------------------- /examples/trac/project/templates/site.html.sample: -------------------------------------------------------------------------------- 1 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /examples/trac/requirements.txt: -------------------------------------------------------------------------------- 1 | Trac==1.0.1 2 | -------------------------------------------------------------------------------- /examples/webpy/index.wsgi: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import sae 4 | import web 5 | 6 | urls = ( 7 | '/', 'Hello' 8 | ) 9 | 10 | app_root = os.path.dirname(__file__) 11 | templates_root = os.path.join(app_root, 'templates') 12 | render = web.template.render(templates_root) 13 | 14 | class Hello: 15 | def GET(self): 16 | return render.hello() 17 | 18 | app = web.application(urls, globals()).wsgifunc() 19 | 20 | application = sae.create_wsgi_app(app) 21 | -------------------------------------------------------------------------------- /examples/webpy/templates/hello.html: -------------------------------------------------------------------------------- 1 | Hello, web.py and templates 2 | -------------------------------------------------------------------------------- /examples/weibo/appstack.py: -------------------------------------------------------------------------------- 1 | 2 | from flask import Flask, request, redirect, session 3 | from weibopy import OAuthHandler, oauth, API 4 | 5 | app = Flask(__name__) 6 | app.debug = True 7 | app.secret_key = 'test' 8 | 9 | consumer_key = '199***' 10 | consumer_secret = 'a1f8****' 11 | 12 | def get_referer(): 13 | return request.headers.get('HTTP_REFERER', '/') 14 | 15 | def get_weibo_user(): 16 | auth = OAuthHandler(consumer_key, consumer_secret) 17 | # Get currrent user access token from session 18 | access_token = session['oauth_access_token'] 19 | auth.setToken(access_token.key, access_token.secret) 20 | api = API(auth) 21 | # Get info from weibo 22 | return api.me() 23 | 24 | def login_ok(f): 25 | def login_wrapper(*args, **kw): 26 | if 'oauth_access_token' not in session: 27 | return redirect('/login') 28 | return f(*args, **kw) 29 | return login_wrapper 30 | 31 | @app.route('/') 32 | @login_ok 33 | def hello(): 34 | user = get_weibo_user() 35 | return "Hello, %s " % (user.screen_name, user.profile_image_url) 36 | 37 | @app.route('/login') 38 | def login(): 39 | session['login_ok_url'] = get_referer() 40 | callback = 'http://appstack.sinaapp.com/login_callback' 41 | 42 | auth = OAuthHandler(consumer_key, consumer_secret, callback) 43 | # Get request token and login url from the provider 44 | url = auth.get_authorization_url() 45 | session['oauth_request_token'] = auth.request_token 46 | # Redirect user to login 47 | return redirect(url) 48 | 49 | @app.route('/login_callback') 50 | def login_callback(): 51 | # This is called by the provider when user has granted permission to your app 52 | verifier = request.args.get('oauth_verifier', None) 53 | auth = OAuthHandler(consumer_key, consumer_secret) 54 | request_token = session['oauth_request_token'] 55 | del session['oauth_request_token'] 56 | 57 | # Show the provider it's us really 58 | auth.set_request_token(request_token.key, request_token.secret) 59 | # Ask for a temporary access token 60 | session['oauth_access_token'] = auth.get_access_token(verifier) 61 | return redirect(session.get('login_ok_url', '/')) 62 | 63 | @app.route('/logout') 64 | def logout(): 65 | del session['oauth_access_token'] 66 | return redirect(get_referer()) 67 | -------------------------------------------------------------------------------- /examples/weibo/index.wsgi: -------------------------------------------------------------------------------- 1 | 2 | import sae 3 | 4 | from appstack import app 5 | 6 | application = sae.create_wsgi_app(app) 7 | 8 | --------------------------------------------------------------------------------