span> 23
+    # error_log /var/log/nginx/tiwen_error.log;
24
+
25
+    # max upload size
26
+    client_max_body_size 75M;   # adjust to taste
27
+
28
+    # JS接口安全域名 & 业务域名 验证
29
+    location /xxx.txt {
30
+        alias /home/diors/work/tiwen/docs/we/xxx.txt;
31
+    }
32
+
33
+    # Django media
34
+    location /media  {
35
+        alias /home/diors/work/tiwen/media;  # your Django project's media files - amend as required
36
+    }
37
+
38
+    location /static {
39
+        alias /home/diors/work/tiwen/collect_static; # your Django project's static files - amend as required
40
+    }
41
+
42
+    # Finally, send all non-media requests to the Django server.
43
+    location / {
44
+        # uwsgi_pass  tiwen;
45
+        proxy_pass  http://tiwen;
46
+        include     /home/diors/work/tiwen/tiwen/deploy/uwsgi_params; # the uwsgi_params file you installed
47
+    }
48
+}
49
+
50
+# configuration of the server
51
+server {
52
+    # the port your site will be served on
53
+    listen      443;
54
+    # the domain name it will serve for
55
+    server_name .a.com; # substitute your machine's IP address or FQDN
56
+    charset     utf-8;
57
+
58
+    # access_log /var/log/nginx/tiwen_ssl_access.log;
59
+    # error_log /var/log/nginx/tiwen_ssl_error.log;
60
+
61
+    ssl on;
62
+    ssl_certificate   cert/214180103970874.pem;
63
+    ssl_certificate_key  cert/214180103970874.key;
64
+    ssl_session_timeout 5m;
65
+    # worker process * exited on signal 11
66
+    # 同一台服务器部署多个 SSL 转发,Nginx 默认是开启 Session 缓存的,导致冲突,验证不通过,连接直接退出
67
+    # 解决方案:禁用 Ningx SSL Session 的缓存
68
+    ssl_session_cache none;
69
+    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
70
+    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
71
+    ssl_prefer_server_ciphers on;
72
+
73
+    # max upload size
74
+    client_max_body_size 75M;   # adjust to taste
75
+
76
+    # JS接口安全域名 & 业务域名 验证
77
+    location /xxx.txt {
78
+        alias /home/diors/work/tiwen/docs/we/xxx.txt;
79
+    }
80
+
81
+    # Django media
82
+    location /media  {
83
+        alias /home/diors/work/tiwen/media;  # your Django project's media files - amend as required
84
+    }
85
+
86
+    location /static {
87
+        alias /home/diors/work/tiwen/collect_static; # your Django project's static files - amend as required
88
+    }
89
+
90
+    # Finally, send all non-media requests to the Django server.
91
+    location / {
92
+        # uwsgi_pass  tiwen;
93
+        proxy_pass  http://tiwen;
94
+        include     /home/diors/work/tiwen/tiwen/deploy/uwsgi_params; # the uwsgi_params file you installed
95
+    }
96
+}

+ 12 - 0
tiwen/deploy.bak/tiwen_supervisor.ini

@@ -0,0 +1,12 @@
1
+[program:tiwen]
2
+command=/home/diors/env/bin/uwsgi --ini /home/diors/work/tiwen/tiwen/deploy/tiwen.ini
3
+autostart=true
4
+autorestart=true
5
+startretries=3
6
+exitcodes=0,1,2
7
+stopsignal=KILL
8
+stopasgroup=true
9
+killasgroup=true
10
+stdout_logfile=/var/log/supervisor_tiwen_access.log
11
+stderr_logfile=/var/log/supervisor_tiwen_error.log
12
+user=diors

+ 12 - 0
tiwen/deploy.bak/tiwen_supervisor2.ini

@@ -0,0 +1,12 @@
1
+[program:tiwen2]
2
+command=/home/diors/env/bin/uwsgi --ini /home/diors/work/tiwen/tiwen/deploy/tiwen2.ini
3
+autostart=true
4
+autorestart=true
5
+startretries=3
6
+exitcodes=0,1,2
7
+stopsignal=KILL
8
+stopasgroup=true
9
+killasgroup=true
10
+stdout_logfile=/var/log/supervisor_tiwen2_access.log
11
+stderr_logfile=/var/log/supervisor_tiwen2_error.log
12
+user=diors

+ 15 - 0
tiwen/deploy.bak/uwsgi_params

@@ -0,0 +1,15 @@
1
+uwsgi_param	QUERY_STRING		$query_string;
2
+uwsgi_param	REQUEST_METHOD		$request_method;
3
+uwsgi_param	CONTENT_TYPE		$content_type;
4
+uwsgi_param	CONTENT_LENGTH		$content_length;
5
+
6
+uwsgi_param	REQUEST_URI		$request_uri;
7
+uwsgi_param	PATH_INFO		$document_uri;
8
+uwsgi_param	DOCUMENT_ROOT		$document_root;
9
+uwsgi_param	SERVER_PROTOCOL		$server_protocol;
10
+uwsgi_param	UWSGI_SCHEME		$scheme;
11
+
12
+uwsgi_param	REMOTE_ADDR		$remote_addr;
13
+uwsgi_param	REMOTE_PORT		$remote_port;
14
+uwsgi_param	SERVER_PORT		$server_port;
15
+uwsgi_param	SERVER_NAME		$server_name;

+ 5 - 0
tiwen/django_file_callback_settings.py

@@ -0,0 +1,5 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+
4
+def DJANGO_FILE_UPLOAD_CALLBACK_FUNC(request, file_path=None, file_url=None):
5
+    """ DJANGO FILE UPLOAD Callback Func """

+ 84 - 0
tiwen/django_we_callback_settings.py

@@ -0,0 +1,84 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+
4
+def DJANGO_WE_CFG_FUNC(request, state=None):
5
+    """ WeChat CFG Callback Func """
6
+
7
+
8
+def DJANGO_WE_QUOTE_STATE_FUNC(request, state):
9
+    """ WeChat Quote Callback Func """
10
+    from utils.redis.connect import r
11
+    return r.quote(state, short_uuid=True)
12
+
13
+
14
+def DJANGO_WE_UNQUOTE_STATE_FUNC(request, state):
15
+    """ WeChat UnQuote Callback Func """
16
+    from utils.redis.connect import r
17
+
18
+    # If Not Buf, When Wechat Multi Request, Unquote Will Get None
19
+    # Then Once Should ReOAuth, Will Raise Error
20
+    return r.unquote(state, buf=True) or state
21
+
22
+
23
+def DJANGO_WE_BASE_FUNC(code, state, access_info=None):
24
+    """ WeChat Base Redirect Callback Func """
25
+
26
+
27
+def DJANGO_WE_BASE_COOKIE_FUNC(code, state, access_info=None):
28
+    """ WeChat Base Set Cookie Redirect Callback Func """
29
+
30
+
31
+def DJANGO_WE_USERINFO_FUNC(code, state, access_info=None, userinfo=None):
32
+    """ WeChat Userinfo Redirect Callback Func """
33
+    from django.conf import settings
34
+
35
+    from utils.redis.connect import r
36
+    from utils.user.userinfo_save import userinfo_save
37
+
38
+    # Save profile or something else
39
+    user = userinfo_save(userinfo)
40
+
41
+    token_check_key = getattr(user, settings.TOKEN_CHECK_KEY)
42
+
43
+    return {
44
+        settings.TOKEN_CHECK_KEY: token_check_key,
45
+        'vtoken': r.token(token_check_key, ex=True, time=r.REDIS_EXPIRED_ONE_DAY, buf=False, short_uuid=True),
46
+    }
47
+
48
+
49
+def DJANGO_WE_USERINFO_COOKIE_FUNC(code, state, access_info=None, userinfo=None):
50
+    """ WeChat Userinfo Set Cookie Redirect Callback Func """
51
+    from django.conf import settings
52
+
53
+    from utils.user.userinfo_save import userinfo_save
54
+
55
+    # Save profile or something else
56
+    user = userinfo_save(userinfo)
57
+
58
+    token_check_key = getattr(user, settings.TOKEN_CHECK_KEY)
59
+
60
+    return {}, settings.TOKEN_CHECK_KEY, token_check_key
61
+
62
+
63
+def DJANGO_WE_SHARE_FUNC(request, state=None):
64
+    """ WeChat Share Callback Func """
65
+    # from django.conf import settings
66
+    # return settings.WECHAT_OAUTH2_REDIRECT_URL
67
+
68
+
69
+def DJANGO_WE_MESSAGE_CALLBACK_FUNC(request, xmldict, decrypted):
70
+    """ WeChat Message Callback Func """
71
+
72
+
73
+def DJANGO_WE_COMPONENT_AUTH_FUNC(request, xmldict, decrypted):
74
+    """ WeChat COMPONENT Auth Func """
75
+
76
+
77
+def DJANGO_WE_COMPONENT_CALLBACK_FUNC(request, appid, xmldict, decrypted):
78
+    """ WeChat COMPONENT Callback Func """
79
+
80
+
81
+def DJANGO_WE_REDIS_OBJ_FUNC(request):
82
+    """ WeChat Redis Object Callback Func """
83
+    from utils.redis.connect import r
84
+    return r

+ 16 - 0
tiwen/func_settings.py

@@ -0,0 +1,16 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import redis_extensions as redis
4
+
5
+
6
+def redis_conf(conf):
7
+    return {
8
+        'host': conf.get('HOST', 'localhost'),
9
+        'port': conf.get('PORT', 6379),
10
+        'password': '{0}:{1}'.format(conf.get('USER', ''), conf.get('PASSWORD', '')) if conf.get('USER') else '',
11
+        'db': conf.get('db', 0),
12
+    }
13
+
14
+
15
+def redis_connect(conf):
16
+    return redis.StrictRedisExtensions(connection_pool=redis.ConnectionPool(**redis_conf(conf)))

+ 17 - 0
tiwen/local_settings_bak.py

@@ -0,0 +1,17 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+# DEBUG = False
4
+
5
+ALLOWED_HOSTS = ['127.0.0.1', 'localhost', 'tiwen']
6
+
7
+# DOMAIN
8
+DOMAIN = 'http://a.com'
9
+
10
+# 邮件设置
11
+# 只有当 DEBUG = False 的时候,才会邮件发送报错信息
12
+SERVER_EMAIL = 'error.notify@exmail.com'
13
+EMAIL_HOST_USER = 'error.notify@exmail.com'
14
+EMAIL_HOST_PASSWORD = '<^_^>pwd<^_^>'
15
+DEFAULT_FROM_EMAIL = 'error.notify <error.notify@exmail.com>'
16
+ADMINS = [('Zhang San', 'san.zhang@exmail.com'), ('Li Si', 'si.li@exmail.com')]
17
+EMAIL_SUBJECT_PREFIX = '[Templet] '

+ 27 - 0
tiwen/local_settings_dev_bak.py

@@ -0,0 +1,27 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import os
4
+
5
+
6
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
7
+PROJ_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
8
+
9
+TEMPLATES = [
10
+    {
11
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
12
+        'DIRS': [os.path.join(BASE_DIR, 'templates')],
13
+        # 'APP_DIRS': True,
14
+        'OPTIONS': {
15
+            'context_processors': [
16
+                'django.template.context_processors.debug',
17
+                'django.template.context_processors.request',
18
+                'django.contrib.auth.context_processors.auth',
19
+                'django.contrib.messages.context_processors.messages',
20
+            ],
21
+            'loaders': [
22
+                'django.template.loaders.filesystem.Loader',
23
+                'django.template.loaders.app_directories.Loader',
24
+            ],
25
+        },
26
+    },
27
+]

+ 401 - 0
tiwen/settings.py

@@ -0,0 +1,401 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+"""
4
+Django settings for tiwen project.
5
+
6
+Generated by 'django-admin startproject' using Django 1.11.3.
7
+
8
+For more information on this file, see
9
+https://docs.djangoproject.com/en/1.11/topics/settings/
10
+
11
+For the full list of settings and their values, see
12
+https://docs.djangoproject.com/en/1.11/ref/settings/
13
+"""
14
+
15
+import os
16
+
17
+# try:
18
+#     from func_settings import redis_connect
19
+#     REDIS_CACHE = redis_connect(REDIS.get('default', {}))
20
+# except ImportError:
21
+#     REDIS_CACHE = None
22
+from django_redis_connector import connector
23
+
24
+
25
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
26
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
27
+PROJ_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
28
+
29
+
30
+# Quick-start development settings - unsuitable for production
31
+# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
32
+
33
+# SECURITY WARNING: keep the secret key used in production secret!
34
+SECRET_KEY = '0=hpv21&am(7(k5ab!^zjvvl=ntj)^i@7)87t47uzumt_5rq$+'
35
+
36
+# SECURITY WARNING: don't run with debug turned on in production!
37
+DEBUG = True
38
+
39
+ALLOWED_HOSTS = []
40
+
41
+
42
+# Application definition
43
+
44
+INSTALLED_APPS = [
45
+    'django.contrib.admin',
46
+    'django.contrib.auth',
47
+    'django.contrib.contenttypes',
48
+    'django.contrib.sessions',
49
+    'django.contrib.messages',
50
+    'django.contrib.staticfiles',
51
+    # 'django_file_upload',
52
+    # 'django_short_url',
53
+    'django_uniapi',
54
+    'django_admin',
55
+    # 'django_rlog',
56
+    'django_we',
57
+    'commands',
58
+    'api',
59
+]
60
+
61
+MIDDLEWARE = [
62
+    'django.middleware.security.SecurityMiddleware',
63
+    'django.contrib.sessions.middleware.SessionMiddleware',
64
+    'django.middleware.common.CommonMiddleware',
65
+    # 'django.middleware.csrf.CsrfViewMiddleware',
66
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
67
+    'django.contrib.messages.middleware.MessageMiddleware',
68
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
69
+    'detect.middleware.UserAgentDetectionMiddleware',
70
+]
71
+
72
+ROOT_URLCONF = 'tiwen.urls'
73
+
74
+TEMPLATES = [
75
+    {
76
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
77
+        'DIRS': [os.path.join(BASE_DIR, 'templates')],
78
+        # 'APP_DIRS': True,
79
+        'OPTIONS': {
80
+            'context_processors': [
81
+                'django.template.context_processors.debug',
82
+                'django.template.context_processors.request',
83
+                'django.contrib.auth.context_processors.auth',
84
+                'django.contrib.messages.context_processors.messages',
85
+            ],
86
+            'loaders': [
87
+                ('django.template.loaders.cached.Loader', [
88
+                    'django.template.loaders.filesystem.Loader',
89
+                    'django.template.loaders.app_directories.Loader',
90
+                ]),
91
+            ],
92
+        },
93
+    },
94
+]
95
+
96
+WSGI_APPLICATION = 'tiwen.wsgi.application'
97
+
98
+
99
+# Database
100
+# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
101
+
102
+DATABASES = {
103
+    # Create Database
104
+    # CREATE DATABASE tiwen DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
105
+    'default': {
106
+        'ENGINE': 'django.db.backends.mysql',
107
+        'NAME': 'tiwen',
108
+        'USER': 'root',
109
+        'PASSWORD': '',
110
+        'HOST': '127.0.0.1',
111
+        'PORT': 3306,
112
+        'CONN_MAX_AGE': 600,
113
+        'OPTIONS': {
114
+            'charset': 'utf8mb4',
115
+        },
116
+    }
117
+}
118
+
119
+
120
+# Password validation
121
+# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
122
+
123
+AUTH_PASSWORD_VALIDATORS = [
124
+    {
125
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
126
+    },
127
+    {
128
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
129
+    },
130
+    {
131
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
132
+    },
133
+    {
134
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
135
+    },
136
+]
137
+
138
+
139
+# Internationalization
140
+# https://docs.djangoproject.com/en/1.11/topics/i18n/
141
+
142
+LANGUAGE_CODE = 'zh-Hans'
143
+
144
+TIME_ZONE = 'Asia/Shanghai'
145
+
146
+USE_I18N = True
147
+
148
+USE_L10N = True
149
+
150
+USE_TZ = True
151
+
152
+
153
+# Static files (CSS, JavaScript, Images)
154
+# https://docs.djangoproject.com/en/1.11/howto/static-files/
155
+
156
+STATICFILES_DIRS = (
157
+    os.path.join(PROJ_DIR, 'static').replace('\\', '/'),
158
+)
159
+
160
+STATIC_ROOT = os.path.join(BASE_DIR, 'collect_static').replace('\\', '/')
161
+
162
+STATIC_URL = '/static/'
163
+
164
+STATICFILES_FINDERS = (
165
+    'django.contrib.staticfiles.finders.FileSystemFinder',
166
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
167
+    # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
168
+)
169
+
170
+MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/')
171
+
172
+MEDIA_URL = '/media/'
173
+
174
+# File 设置
175
+FILE_UPLOAD_MAX_MEMORY_SIZE = 5242880  # InMemoryUploadedFile 文件最大值,设置为 5 MB
176
+FILE_UPLOAD_PERMISSIONS = 0o644  # TemporaryUploadedFile 文件权限设置
177
+
178
+# DOMAIN
179
+DOMAIN = 'http://a.com'
180
+
181
+# Redis 设置
182
+REDIS = {
183
+    'default': {
184
+        'HOST': '127.0.0.1',
185
+        'PORT': 6379,
186
+        'USER': '',
187
+        'PASSWORD': '',
188
+        'db': 0,
189
+        'decode_responses': True,
190
+    }
191
+}
192
+
193
+# 微信设置
194
+WECHAT = {
195
+    'JSAPI': {
196
+        'trade_type': 'JSAPI',  # JSAPI-网页支付、NATIVE-原生支付、APP-APP支付、MICROPAY-刷卡支付
197
+        'token': '5201314',
198
+        'appID': '',
199
+        'appsecret': '',
200
+        'encodingaeskey': '',
201
+        'mchID': '',
202
+        'apiKey': '',
203
+        'mch_cert': '',
204
+        'mch_key': '',
205
+        'redpack': {
206
+
207
+        }
208
+    },
209
+}
210
+
211
+WECHAT_DEFAULT_CFG = 'JSAPI'
212
+
213
+# 微信唯一标识
214
+# Choices: 'unionid' or 'openid'
215
+#
216
+# models.py
217
+#   'unique_identifier': self.unionid if settings.WECHAT_UNIQUE_IDENTIFICATION == 'unionid' else self.openid,
218
+# views.py
219
+#   unique_identifier = request.POST.get(settings.WECHAT_UNIQUE_IDENTIFICATION, '')
220
+#   profile = Profile.objects.get(**{settings.WECHAT_UNIQUE_IDENTIFICATION: unique_identifier})
221
+#
222
+# If not bind to OpenPlat, change `WECHAT_UNIQUE_IDENTIFICATION` as `openid`
223
+WECHAT_UNIQUE_IDENTIFICATION = 'unionid'
224
+
225
+# Token 错误重授权设置
226
+TOKEN_CHECK_KEY = ''
227
+# TOKEN_CHECK_KEY = 'user_id'
228
+WECHAT_OAUTH2_REDIRECT_ENTRY = ''
229
+WECHAT_OAUTH2_REDIRECT_URL = ''
230
+
231
+# Cookie 设置
232
+DJANGO_WE_COOKIE_MAX_AGE = COOKIE_MAX_AGE = 31536000  # 单位:秒,1年:365 * 24 * 60 * 60 = 31536000
233
+DJANGO_WE_COOKIE_SALT = COOKIE_SALT = 'djwe'  # Salt for ``set_signed_cookie``
234
+# Cookie 校验设置
235
+COOKIE_USER_CHECK_KEY = ''
236
+# COOKIE_USER_CHECK_KEY = 'user_id'
237
+
238
+# 邮件设置
239
+# https://docs.djangoproject.com/en/1.11/howto/error-reporting/#email-reports
240
+# When DEBUG is False, Django will email the users listed in the ADMINS setting
241
+# whenever your code raises an unhandled exception and results in an internal server error (HTTP status code 500).
242
+# 只有当 DEBUG = False 的时候,才会邮件发送报错信息
243
+# Email address that error messages come from.
244
+SERVER_EMAIL = 'error.notify@exmail.com'
245
+# The email backend to use. For possible shortcuts see django.core.mail.
246
+# The default is to use the SMTP backend.
247
+# Third-party backends can be specified by providing a Python path
248
+# to a module that defines an EmailBackend class.
249
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
250
+# Host for sending email.
251
+EMAIL_HOST = 'smtp.exmail.qq.com'
252
+# Port for sending email.
253
+EMAIL_PORT = 25
254
+# Optional SMTP authentication information for EMAIL_HOST.
255
+EMAIL_HOST_USER = 'error.notify@exmail.com'
256
+EMAIL_HOST_PASSWORD = '<^_^>pwd<^_^>'
257
+EMAIL_USE_TLS = False
258
+EMAIL_USE_SSL = False
259
+EMAIL_SSL_CERTFILE = None
260
+EMAIL_SSL_KEYFILE = None
261
+# Default: None
262
+# Specifies a timeout in seconds for blocking operations like the connection attempt.
263
+# socket.error: [Errno 110] Connection timed out
264
+# 如果不设置 EMAIL_TIMEOUT,一旦 sock.connect 连接有问题 error: (110, 'Connection timed out')
265
+# 就会导致进程一直卡住,最终导致服务崩溃
266
+# EMAIL_TIMEOUT = None
267
+EMAIL_TIMEOUT = 3
268
+# Default email address to use for various automated correspondence from
269
+# the site managers.
270
+DEFAULT_FROM_EMAIL = 'error.notify <error.notify@exmail.com>'
271
+# People who get code error notifications.
272
+# In the format [('Full Name', 'email@example.com'), ('Full Name', 'anotheremail@example.com')]
273
+# Default: [], not send code error notifications.
274
+# https://github.com/django/django/blob/5fcfe5361e5b8c9738b1ee4c1e9a6f293a7dda40/django/core/mail/__init__.py#L90
275
+# ADMINS = [('Zhang San', 'san.zhang@exmail.com'), ('Li Si', 'si.li@exmail.com')]
276
+ADMINS = []
277
+# Not-necessarily-technical managers of the site. They get broken link
278
+# notifications and other various emails.
279
+MANAGERS = ADMINS
280
+# Subject-line prefix for email messages send with django.core.mail.mail_admins
281
+# or ...mail_managers.  Make sure to include the trailing space.
282
+EMAIL_SUBJECT_PREFIX = '[Templet] '
283
+
284
+# Django-Admin Settings
285
+DJANGO_ADMIN_DISABLE_DELETE_SELECTED = False
286
+
287
+# Django-FILE-UPLOAD Settings
288
+DJANGO_FILE_UPLOAD_USE_YM = True
289
+DJANGO_FILE_UPLOAD_USE_DT = True
290
+
291
+# Django-Logit Settings
292
+DJANGO_LOGIT_ENABLED = True
293
+DJANGO_LOGIT_BODY_FLAG = False
294
+DJANGO_LOGIT_RES_FLAG = False
295
+
296
+# Django-Onerror Settings
297
+DJANGO_ONERROR_ACCEPT_REPORT = True
298
+DJANGO_ONERROR_ADMIN_SITE_REGISTER = True
299
+
300
+# Django-Short-URL Settings
301
+# Redirect url when short url not exists
302
+DJANGO_SHORT_URL_REDIRECT_URL = ''
303
+
304
+# Django-We Settings
305
+DJANGO_WE_QUOTE_OR_NOT = True
306
+DJANGO_WE_MODEL_DISPLAY_OR_NOT = True
307
+# Enable Cookie or not
308
+# DJANGO_WE_BASE_REDIRECT_SET_COOKIE = False
309
+# DJANGO_WE_USERINFO_REDIRECT_SET_COOKIE = True
310
+# Cookie Config
311
+DJANGO_WE_COOKIE_MAX_AGE = COOKIE_MAX_AGE
312
+DJANGO_WE_COOKIE_SALT = COOKIE_SALT
313
+
314
+# 开发调试相关配置
315
+if DEBUG:
316
+    try:
317
+        from .local_settings_dev import *
318
+    except ImportError:
319
+        pass
320
+
321
+try:
322
+    from .local_settings import *
323
+except ImportError:
324
+    pass
325
+
326
+try:
327
+    from .django_file_callback_settings import *
328
+except ImportError:
329
+    pass
330
+
331
+try:
332
+    from .django_we_callback_settings import *
333
+except ImportError:
334
+    pass
335
+
336
+# 依赖 local_settings 中的配置
337
+# 微信授权设置
338
+# WECHAT_OAUTH2_REDIRECT_URI = '{0}/we/oauth2?scope={{0}}&redirect_url={{1}}'.format(DOMAIN)
339
+# Shorten URL
340
+# ``o`` is short for oauth2
341
+# ``r`` is short for redirect_url
342
+WECHAT_OAUTH2_REDIRECT_URI = '{0}/we/o?scope={{0}}&r={{1}}'.format(DOMAIN)
343
+WECHAT_OAUTH2_USERINFO_REDIRECT_URI = '{0}/we/o?r={{0}}'.format(DOMAIN)  # Scope default snsapi_userinfo
344
+WECHAT_BASE_REDIRECT_URI = '{0}/we/base_redirect'.format(DOMAIN)
345
+WECHAT_USERINFO_REDIRECT_URI = '{0}/we/userinfo_redirect'.format(DOMAIN)
346
+WECHAT_DIRECT_BASE_REDIRECT_URI = '{0}/we/direct_base_redirect'.format(DOMAIN)
347
+WECHAT_DIRECT_USERINFO_REDIRECT_URI = '{0}/we/direct_userinfo_redirect'.format(DOMAIN)
348
+
349
+# Redis 连接
350
+WECHAT_REDIS_OBJ = REDIS_CACHE = connector(REDIS.get('default', {}))
351
+
352
+# LOGGER 设置
353
+# python manage.py rlistlog --key=django:logit:tiwen --filename=/tmp/tiwen.logit.log
354
+LOGGING = {
355
+    'version': 1,
356
+    'disable_existing_loggers': False,
357
+    'formatters': {
358
+        'verbose': {
359
+            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
360
+        },
361
+        'simple': {
362
+            'format': '%(levelname)s %(message)s'
363
+        },
364
+    },
365
+    'handlers': {
366
+        'logit': {
367
+            'level': 'DEBUG',
368
+            'class': 'rlog.RedisListHandler',
369
+            'redis_client': REDIS_CACHE,
370
+            'key': 'django:logit:tiwen',
371
+            'formatter': 'verbose',
372
+        },
373
+        'console': {
374
+            'level': 'DEBUG',
375
+            'class': 'logging.StreamHandler',
376
+            'formatter': 'verbose'
377
+        },
378
+    },
379
+    'loggers': {
380
+        'logit': {
381
+            'handlers': ['logit'],
382
+            'level': 'DEBUG',
383
+            'propagate': True,
384
+        },
385
+        'console': {
386
+            'handlers': ['console'],
387
+            'level': 'DEBUG',
388
+            'propagate': True,
389
+        },
390
+    },
391
+}
392
+
393
+# MAX_BIGINT
394
+# Why Not ``sys.maxint``
395
+# n bit platform sys.maxint = 2 ** (n - 1) - 1
396
+# 64 bit 9223372036854775807, 32 bit 2147483647
397
+from django.db.models import BigIntegerField  # isort:skip
398
+MAX_BIGINT = BigIntegerField.MAX_BIGINT
399
+
400
+# DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
401
+DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

+ 68 - 0
tiwen/static/tiwen/css/common.css

@@ -0,0 +1,68 @@
1
+.text-center {
2
+    text-align: center;
3
+}
4
+
5
+/*
6
+  文本溢出省略,元素需要设置定宽度
7
+  */
8
+/* 单行 */
9
+.text_ellipsis {
10
+    overflow: hidden;
11
+    white-space: nowrap;
12
+    text-overflow: ellipsis;
13
+}
14
+/* 多行 */
15
+.a {
16
+    overflow: hidden;
17
+    text-overflow: ellipsis;
18
+    display: -webkit-box;
19
+    -webkit-line-clamp: 2;
20
+    -webkit-box-orient: vertical;
21
+}
22
+
23
+/*
24
+  浮动 & 清除浮动
25
+ */
26
+.fl {
27
+    float: left;
28
+}
29
+
30
+.fr {
31
+    float: right;
32
+}
33
+
34
+.clearfix {
35
+    zoom: 1;
36
+}
37
+
38
+.clearfix:after {
39
+    content: "\200B";
40
+    display: block;
41
+    height: 0;
42
+    clear: both;
43
+}
44
+
45
+.clearfix:after {content:"."; display:block; height:0; visibility:hidden; clear:both; }
46
+.clearfix { *zoom:1; }
47
+
48
+/*
49
+  解决安卓微信点击图片预览问题
50
+  这个会让 img 标签的点击事件失效,如果想要点击图片就要给上面再写一层
51
+  需要长按识别二维码的时候,则再单独给该图片添加
52
+  ```css
53
+  pointer-events: auto;
54
+  ```
55
+  来恢复对应的点击事件
56
+  */
57
+img {
58
+    pointer-events: none;
59
+}
60
+
61
+.clickable {
62
+    pointer-events: auto;
63
+}
64
+
65
+/* Input 自动填充后,移除 Chrome 默认黄色 */
66
+input:-webkit-autofill {
67
+    box-shadow: 0 0 0 1000px white inset !important;
68
+}

+ 574 - 0
tiwen/static/tiwen/js/jswe.js

@@ -0,0 +1,574 @@
1
+!(function(e, t) {
2
+    var config = {
3
+        wxconfig: 'http://api.tt4it.com/wx/jsapi_signature',
4
+        callback: 'callback'
5
+    }, wxData = {
6
+        debug: false,
7
+        imgUrl: '',
8
+        link: '',
9
+        desc: '',
10
+        title: '',
11
+        timeLine: ''
12
+    }, wxConfig = {
13
+        hide: false,
14
+        baseFlag: false,
15
+        baseHide: false,
16
+        close: false,
17
+        hideMenuItems: [],
18
+        showMenuItems: []
19
+    }, jsApiList = [
20
+        'checkJsApi',
21
+        'onMenuShareTimeline',
22
+        'onMenuShareAppMessage',
23
+        'onMenuShareQQ',
24
+        'onMenuShareWeibo',
25
+        'onMenuShareQZone',
26
+        'hideMenuItems',
27
+        'showMenuItems',
28
+        'hideAllNonBaseMenuItem',
29
+        'showAllNonBaseMenuItem',
30
+        'translateVoice',
31
+        'startRecord',
32
+        'stopRecord',
33
+        'onRecordEnd',
34
+        'playVoice',
35
+        'pauseVoice',
36
+        'stopVoice',
37
+        'uploadVoice',
38
+        'downloadVoice',
39
+        'chooseImage',
40
+        'previewImage',
41
+        'uploadImage',
42
+        'downloadImage',
43
+        'getLocalImgData',
44
+        'getNetworkType',
45
+        'openLocation',
46
+        'getLocation',
47
+        'hideOptionMenu',
48
+        'showOptionMenu',
49
+        'closeWindow',
50
+        'scanQRCode',
51
+        'chooseWXPay',
52
+        'openEnterpriseRedPacket',
53
+        'openProductSpecificView',
54
+        'addCard',
55
+        'chooseCard',
56
+        'openCard'
57
+    ], wxApiFun
58
+
59
+    function isEmpty(obj) {
60
+        if (obj == null) return true
61
+        if (obj.length > 0) return false
62
+        if (obj.length === 0) return true
63
+        for (var key in obj) {
64
+            if (Object.prototype.hasOwnProperty.call(obj, key)) return false
65
+        }
66
+        return true
67
+    }
68
+
69
+    function isNotEmpty(obj) {
70
+        return !isEmpty(obj)
71
+    }
72
+
73
+    function isOpenOnPC() {  // 判断当前网页是否在 PC 浏览器中打开
74
+        var ua = navigator.userAgent
75
+        return /windows nt/i.test(ua) || /macintosh/i.test(ua) || /linux x86_64/i.test(ua)
76
+    }
77
+
78
+    function isOpenInWeixin() {  // 判断当前网页是否在微信内置浏览器中打开
79
+        return /micromessenger/i.test(navigator.userAgent)
80
+    }
81
+
82
+    function getWeixinVersion() {
83
+        var ua = navigator.userAgent,
84
+            mt = ua.match(/micromessenger\/([\d.]+)/i)
85
+        return (mt ? mt[1] : '')
86
+    }
87
+
88
+    // This function checks whether Wechat is the appointed version or not
89
+    // Cmp: http://jsperf.com/regexp-test-vs-indexof-ignore-upper-and-lower
90
+    function isWeixinVersion(version) {
91
+        // return new RegExp('micromessenger/' + version , 'i').test(navigator.userAgent)
92
+        return navigator.userAgent.toLowerCase().indexOf('micromessenger/' + version) != -1
93
+    }
94
+
95
+    function hideOptionMenu() {
96
+        wxConfig.hide = true
97
+        fixedWxData()
98
+    }
99
+
100
+    function showOptionMenu() {
101
+        wxConfig.hide = false
102
+        fixedWxData()
103
+    }
104
+
105
+    function hideMenuItems(items) {
106
+        wxConfig.hideMenuItems = items
107
+        fixedWxData()
108
+    }
109
+
110
+    function showMenuItems(items) {
111
+        wxConfig.showMenuItems = items
112
+        fixedWxData()
113
+    }
114
+
115
+    function hideAllNonBaseMenuItem() {
116
+        wxConfig.baseFlag = true
117
+        wxConfig.baseHide = true
118
+        fixedWxData()
119
+    }
120
+
121
+    function showAllNonBaseMenuItem() {
122
+        wxConfig.baseFlag = true
123
+        wxConfig.baseHide = false
124
+        fixedWxData()
125
+    }
126
+
127
+    function closeWindow() {
128
+        wxConfig.close = true
129
+        fixedWxData()
130
+    }
131
+
132
+    function wxReady(data) {
133
+        data = typeof data === 'object' ? data : JSON.parse(data)
134
+        wx.config({
135
+            debug: wxData.debug,
136
+            appId: data.appId,
137
+            timestamp: data.timestamp,
138
+            nonceStr: data.nonceStr,
139
+            signature: data.signature,
140
+            jsApiList: jsApiList
141
+        })
142
+
143
+        var callbacks = {
144
+            trigger: function (res) {
145
+                // alert('用户点击发送给朋友')
146
+                if (JSWE.wxTrigger) {JSWE.wxTrigger(res)}
147
+            },
148
+            success: function (res) {
149
+                // alert('已分享')
150
+                if (JSWE.wxSuccess) {JSWE.wxSuccess(res)}
151
+            },
152
+            cancel: function (res) {
153
+                // alert('已取消')
154
+                if (JSWE.wxCancel) {JSWE.wxCancel(res)}
155
+            },
156
+            fail: function (res) {
157
+                // alert(JSON.stringify(res))
158
+                if (JSWE.wxFail) {JSWE.wxFail(res)}
159
+            }
160
+        }, shareInfo = function(flag) {
161
+            var _share = {
162
+                title: flag ? wxData.title : (wxData.timeLine || wxData.desc),
163
+                link: wxData.link,
164
+                imgUrl: wxData.imgUrl,
165
+                trigger: callbacks.trigger,
166
+                success: callbacks.success,
167
+                cancel: callbacks.cancel,
168
+                fail: callbacks.fail
169
+            }
170
+            if (flag) _share.desc = wxData.desc
171
+            return _share
172
+        }, wxShareApi = function() {
173
+            // 2. 分享接口
174
+            // 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口
175
+            wx.onMenuShareAppMessage(shareInfo(1))
176
+            // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口
177
+            wx.onMenuShareTimeline(shareInfo(0))
178
+            // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口
179
+            wx.onMenuShareQQ(shareInfo(1))
180
+            // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口
181
+            wx.onMenuShareWeibo(shareInfo(1))
182
+            // 2.5 监听“分享到QQ空间”按钮点击、自定义分享内容及分享结果接口
183
+            wx.onMenuShareQZone(shareInfo(1))
184
+        }, wxMenuApi = function () {
185
+            // 8. 界面操作接口
186
+            // 8.1 隐藏右上角菜单
187
+            // 8.2 显示右上角菜单
188
+            if (wxConfig.hide) {wx.hideOptionMenu()} else {wx.showOptionMenu()}
189
+            // 8.3 批量隐藏菜单项
190
+            if (isNotEmpty(wxConfig.hideMenuItems)) {
191
+                wx.hideMenuItems({
192
+                    menuList: wxConfig.hideMenuItems,
193
+                    success: function (res) {
194
+                        if (JSWE.wxHideMenuItemsSuccess) {JSWE.wxHideMenuItemsSuccess(res)}
195
+                    },
196
+                    fail: function (res) {
197
+                        if (JSWE.wxHideMenuItemsFail) {JSWE.wxHideMenuItemsFail(res)}
198
+                    }
199
+                })
200
+            }
201
+            // 8.4 批量显示菜单项
202
+            if (isNotEmpty(wxConfig.showMenuItems)) {
203
+                wx.showMenuItems({
204
+                    menuList: wxConfig.showMenuItems,
205
+                    success: function (res) {
206
+                        if (JSWE.wxShowMenuItemsSuccess) {JSWE.wxShowMenuItemsSuccess(res)}
207
+                    },
208
+                    fail: function (res) {
209
+                        if (JSWE.wxShowMenuItemsFail) {JSWE.wxShowMenuItemsFail(res)}
210
+                    }
211
+                })
212
+            }
213
+            // 8.5 隐藏所有非基本菜单项
214
+            // 8.6 显示所有被隐藏的非基本菜单项
215
+            if (wxConfig.baseFlag) {
216
+                if (wxConfig.baseHide) {wx.hideAllNonBaseMenuItem()} else {wx.showAllNonBaseMenuItem()}
217
+            }
218
+            // 8.7 关闭当前窗口
219
+            if (wxConfig.close) {wx.closeWindow()}
220
+        }, wxVoiceApi = function() {
221
+            // 4.3 监听录音自动停止
222
+            wx.onVoiceRecordEnd({
223
+                complete: function (res) {
224
+                    voice.localId = res.localId
225
+                    if (JSWE.wxVoiceRecordEnd) {JSWE.wxVoiceRecordEnd(res)}
226
+                }
227
+            })
228
+            // 4.7 监听录音播放停止
229
+            wx.onVoicePlayEnd({
230
+                complete: function (res) {
231
+                    if (JSWE.wxVoicePlayEnd) {JSWE.wxVoicePlayEnd(res)}
232
+                }
233
+            })
234
+        }, wxApi = function () {
235
+            wxShareApi()
236
+            wxMenuApi()
237
+            wxVoiceApi()
238
+        }
239
+
240
+        wx.ready(wxApi)
241
+
242
+        return wxApiFun = wxApi
243
+    }
244
+
245
+    if (isOpenInWeixin() || isOpenOnPC()) {
246
+        if ('undefined' !== typeof JSWE_CONF_UPDATE) JSWE_CONF_UPDATE(config)
247
+        $.ajax({
248
+            url: config.wxconfig,
249
+            type: 'get',
250
+            dataType: 'jsonp',
251
+            jsonpCallback: config.callback,
252
+            data: {
253
+                url: window.location.href.split('#')[0]
254
+            },
255
+            success: wxReady
256
+        })
257
+    }
258
+
259
+    function initWxData(data, flag) {
260
+        for(var d in data) {if (d in wxData) wxData[d] = data[d]}
261
+        if (flag) fixedWxData()
262
+    }
263
+
264
+    function changeWxData(key, value, flag) {
265
+        if (key in falDwxDataata) {wxData[key] = value}
266
+        if (flag) fixedWxData()
267
+    }
268
+
269
+    function fixedWxData() {
270
+        if ('undefined' !== typeof wxApiFun) wxApiFun()
271
+    }
272
+
273
+    // 3 智能接口
274
+    var voice = {
275
+        localId: '',
276
+        serverId: ''
277
+    }
278
+    // 3.1 识别音频并返回识别结果
279
+    function translateVoice() {
280
+        if (voice.localId == '') {
281
+            if (JSWE.wxTranslateVoiceEmpty) {JSWE.wxTranslateVoiceEmpty()}
282
+            return
283
+        }
284
+        wx.translateVoice({
285
+            localId: voice.localId,
286
+            complete: function (res) {
287
+                if (JSWE.wxTranslateVoiceComplete) {JSWE.wxTranslateVoiceComplete(res)}
288
+            }
289
+        })
290
+    }
291
+
292
+    // 4 音频接口
293
+    // 4.1 开始录音
294
+    function startRecord() {
295
+        wx.startRecord({
296
+            cancel: function () {
297
+                if (JSWE.wxStartRecordCancel) {JSWE.wxStartRecordCancel(res)}
298
+            }
299
+        })
300
+    }
301
+
302
+    // 4.2 停止录音
303
+    function stopRecord() {
304
+        wx.stopRecord({
305
+          success: function (res) {
306
+              voice.localId = res.localId
307
+              if (JSWE.wxStopRecordSuccess) {JSWE.wxStopRecordSuccess(res)}
308
+          },
309
+          fail: function (res) {
310
+              if (JSWE.wxStopRecordFail) {JSWE.wxStopRecordFail(res)}
311
+          }
312
+        })
313
+    }
314
+
315
+    // 4.4 播放音频
316
+    function playVoice() {
317
+        if (voice.localId == '') {
318
+            if (JSWE.wxPlayVoiceEmpty) {JSWE.wxPlayVoiceEmpty()}
319
+            return
320
+        }
321
+        wx.playVoice({
322
+            localId: voice.localId
323
+        })
324
+    }
325
+
326
+    // 4.5 暂停播放音频
327
+    function pauseVoice() {
328
+        if (voice.localId == '') {
329
+            if (JSWE.wxPauseVoiceEmpty) {JSWE.wxPauseVoiceEmpty()}
330
+            return
331
+        }
332
+        wx.pauseVoice({
333
+            localId: voice.localId
334
+        })
335
+    }
336
+
337
+    // 4.6 停止播放音频
338
+    function stopVoice() {
339
+        if (voice.localId == '') {
340
+            if (JSWE.wxStopVoiceEmpty) {JSWE.wxStopVoiceEmpty()}
341
+            return
342
+        }
343
+        wx.stopVoice({
344
+            localId: voice.localId
345
+        })
346
+    }
347
+
348
+    // 4.8 上传语音
349
+    function uploadVoice() {
350
+        var localId = voice.localId
351
+        if (localId == '') {
352
+            if (JSWE.wxUploadVoiceEmpty) {JSWE.wxUploadVoiceEmpty()}
353
+            return
354
+        }
355
+        wx.uploadVoice({
356
+            localId: localId,
357
+            success: function (res) {
358
+                voice.serverId = res.serverId
359
+                if (JSWE.wxUploadVoiceSuccess) {JSWE.wxUploadVoiceSuccess(res, localId)}
360
+            }
361
+        })
362
+    }
363
+
364
+    // 4.9 下载语音
365
+    function downloadVoice() {
366
+        var serverId = voice.serverId
367
+        if (serverId == '') {
368
+            if (JSWE.wxDownloadVoiceEmpty) {JSWE.wxDownloadVoiceEmpty()}
369
+            return
370
+        }
371
+        wx.downloadVoice({
372
+            serverId: serverId,
373
+            success: function (res) {
374
+                voice.localId = res.localId
375
+                if (JSWE.wxDownloadVoiceSuccess) {JSWE.wxDownloadVoiceSuccess(res, serverId)}
376
+            }
377
+        })
378
+    }
379
+
380
+    // 5 图片接口
381
+    var images = {
382
+        localIds: [],
383
+        serverIds: []
384
+    }
385
+    // 5.1 拍照、本地选图
386
+    function chooseImage(choose_params) {
387
+        if ('undefined' === typeof choose_params) choose_params = {}
388
+        wx.chooseImage({
389
+            count: choose_params.count || 9, // 默认9
390
+            sizeType: choose_params.sizeType || ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
391
+            sourceType: choose_params.sourceType || ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
392
+            success: function (res) {
393
+                images.localIds = res.localIds // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
394
+                // 判断是否直接上传
395
+                if (choose_params.directUpload) {setTimeout(uploadImages({localIds: images.localIds, isShowProgressTips: choose_params.isShowProgressTips || 1}), 100)}
396
+                // 拍照、本地选图成功后的回调函数
397
+                if (JSWE.wxChooseImageSuccess) {JSWE.wxChooseImageSuccess(res, choose_params.extras || {})}
398
+            }
399
+        })
400
+    }
401
+
402
+    // 5.2 图片预览
403
+    function previewImage(preview_params) {
404
+        wx.previewImage({
405
+            current: preview_params.current, // 当前显示图片的链接,不填则默认为 urls 的第一张
406
+            urls: preview_params.urls // 需要预览的图片链接列表
407
+        })
408
+    }
409
+
410
+    // 5.3 上传图片
411
+    function uploadImage(upload_params) {
412
+        // 上传图片为异步处理,重复上传同一图片,返回的serverId也是不同的
413
+        var localId = upload_params.localId
414
+        wx.uploadImage({
415
+            localId: localId, // 需要上传的图片的本地ID,由chooseImage接口获得
416
+            isShowProgressTips: upload_params.isShowProgressTips || 1, // 默认为1,显示进度提示
417
+            success: function (res) {
418
+                images.serverIds.push(res.serverId) // 返回图片的服务器端ID
419
+                // 上传图片成功后的回调函数
420
+                if (JSWE.wxUploadImageSuccess) {JSWE.wxUploadImageSuccess(res, localId)}
421
+            }
422
+        })
423
+    }
424
+
425
+    function uploadImages(upload_params) {
426
+        var localIds = upload_params.localIds, isShowProgressTips = upload_params.isShowProgressTips || 1
427
+        images.serverIds = []
428
+        for (var idx in localIds) {uploadImage({localId: localIds[idx], isShowProgressTips: isShowProgressTips})}
429
+    }
430
+
431
+    // 5.4 下载图片
432
+    function downloadImage(download_params) {
433
+        var serverId = download_params.serverId
434
+        wx.downloadImage({
435
+            serverId: serverId, // 需要下载的图片的服务器端ID,由uploadImage接口获得
436
+            isShowProgressTips: download_params.isShowProgressTips || 1, // 默认为1,显示进度提示
437
+            success: function (res) {
438
+                images.localId.push(res.localId)
439
+                if (JSWE.wxDownloadImageSuccess) {JSWE.wxDownloadImageSuccess(res, serverId)}
440
+            }
441
+        })
442
+    }
443
+
444
+    function downloadImages(download_params) {
445
+        var serverIds = download_params.serverIds, isShowProgressTips = download_params.isShowProgressTips || 1
446
+        images.localIds = []
447
+        for (var idx in serverIds) {downloadImage({serverId: serverIds[idx], isShowProgressTips: isShowProgressTips})}
448
+    }
449
+
450
+    function getLocalImgData(localId) {
451
+        wx.getLocalImgData({
452
+            localId: localId, // 图片的localID
453
+            success: function (res) {
454
+                // var localData = res.localData; // localData是图片的base64数据,可以用img标签显示
455
+                if (JSWE.wxGetLocalImgDataSuccess) {JSWE.wxGetLocalImgDataSuccess(res)}
456
+            }
457
+        })
458
+    }
459
+
460
+    // 9 微信原生接口
461
+    // 9.1.1 扫描二维码并返回结果
462
+    // 9.1.2 扫描二维码并返回结果
463
+    function scanQRCode(scan_params) {
464
+        if ('undefined' === typeof scan_params) scan_params = {}
465
+        wx.scanQRCode({
466
+            needResult: scan_params.needResult || 0,  // 默认为0,0扫描结果由微信处理,1直接返回扫描结果
467
+            scanType: scan_params.scanType || ['qrCode', 'barCode'],  // 可以指定扫二维码还是一维码,默认二者都有
468
+            success: function (res) {  // 当 needResult 为 1 时,扫码返回的结果
469
+                if (JSWE.wxScanQRCodeSuccess) {JSWE.wxScanQRCodeSuccess(res)}
470
+            }
471
+        })
472
+    }
473
+
474
+    // QRCode & BarCode is different
475
+    function parseScanQRCodeResultStr(resultStr) {
476
+        var strs = resultStr.split(',')
477
+        return strs[strs.length - 1]
478
+    }
479
+
480
+    // 10 微信支付接口
481
+    // 10.1 发起一个支付请求
482
+    function chooseWXPay(wxpay_params) {
483
+        wx.chooseWXPay({
484
+            timestamp: wxpay_params.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
485
+            nonceStr: wxpay_params.nonceStr, // 支付签名随机串,不长于 32 位
486
+            package: wxpay_params.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
487
+            signType: wxpay_params.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
488
+            paySign: wxpay_params.paySign, // 支付签名
489
+            success: function (res) {
490
+                // 支付成功后的回调函数
491
+                if (JSWE.wxPaySuccess) {JSWE.wxPaySuccess(res)}
492
+            }
493
+        })
494
+    }
495
+
496
+    // xx 微信原生企业红包接口
497
+    // xx.1 发起一个发送原生企业红包请求
498
+    function openEnterpriseRedPacket(wxredpack_params) {
499
+        wx.openEnterpriseRedPacket({
500
+            timeStamp: wxredpack_params.timeStamp, // 红包签名时间戳,注意原生企业红包接口timeStamp字段名需大写其中的S字符,而支付接口timeStamp字段名无需大写其中的S字符。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
501
+            nonceStr: wxredpack_params.nonceStr, // 红包签名随机串,不长于 32 位
502
+            package: encodeURIComponent(wxredpack_params.package), // 发放红包接口返回的prepay_id参数值,提交格式如:prepay_id=***)
503
+            signType: wxredpack_params.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
504
+            paySign: wxredpack_params.paySign, // 红包签名
505
+            success: function (res) {
506
+                // 发送原生企业红包成功后的回调函数
507
+                if (JSWE.wxEnterpriseRedPacketSuccess) {JSWE.wxEnterpriseRedPacketSuccess(res)}
508
+            }
509
+        })
510
+    }
511
+
512
+    var v = {
513
+        version: '1.0.5',
514
+
515
+        // Basic Vars
516
+        config: config,
517
+        wxData: wxData,
518
+        jsApiList: jsApiList,
519
+
520
+        isEmpty: isEmpty,
521
+        isNotEmpty: isNotEmpty,
522
+
523
+        // Weixin Function
524
+        isOpenInWeixin: isOpenInWeixin,
525
+        getWeixinVersion: getWeixinVersion,
526
+        isWeixinVersion: isWeixinVersion,
527
+
528
+        // Menu Function
529
+        hideOptionMenu: hideOptionMenu,
530
+        showOptionMenu: showOptionMenu,
531
+        hideMenuItems: hideMenuItems,
532
+        showMenuItems: showMenuItems,
533
+        hideAllNonBaseMenuItem: hideAllNonBaseMenuItem,
534
+        showAllNonBaseMenuItem: showAllNonBaseMenuItem,
535
+        closeWindow: closeWindow,
536
+
537
+        // Share Function
538
+        initWxData: initWxData,
539
+        changeWxData: changeWxData,
540
+        fixedWxData: fixedWxData,
541
+
542
+        // Voice Function
543
+        voice: voice,
544
+        translateVoice: translateVoice,
545
+        startRecord: startRecord,
546
+        stopRecord: stopRecord,
547
+        playVoice: playVoice,
548
+        pauseVoice: pauseVoice,
549
+        stopVoice: stopVoice,
550
+        uploadVoice: uploadVoice,
551
+        downloadVoice: downloadVoice,
552
+
553
+        // Image Function
554
+        images: images,
555
+        chooseImage: chooseImage,
556
+        previewImage: previewImage,
557
+        uploadImage: uploadImage,
558
+        uploadImages: uploadImages,
559
+        downloadImage: downloadImage,
560
+        downloadImages: downloadImages,
561
+        getLocalImgData: getLocalImgData,
562
+
563
+        // Scan Function
564
+        scanQRCode: scanQRCode,
565
+        parseScanQRCodeResultStr: parseScanQRCodeResultStr,
566
+
567
+        // Pay Function
568
+        chooseWXPay: chooseWXPay,
569
+
570
+        // EnterpriseRedPacket Function
571
+        openEnterpriseRedPacket: openEnterpriseRedPacket
572
+    }
573
+    e.JSWE = e.V = v
574
+})(window)

+ 56 - 0
tiwen/urls.py

@@ -0,0 +1,56 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+"""tiwen URL Configuration
4
+
5
+The `urlpatterns` list routes URLs to views. For more information please see:
6
+    https://docs.djangoproject.com/en/1.11/topics/http/urls/
7
+Examples:
8
+Function views
9
+    1. Add an import:  from my_app import views
10
+    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
11
+Class-based views
12
+    1. Add an import:  from other_app.views import Home
13
+    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
14
+Including another URLconf
15
+    1. Import the include() function: from django.conf.urls import url, include
16
+    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
17
+"""
18
+from django.conf import settings
19
+from django.conf.urls.static import static
20
+from django.contrib import admin
21
+from django.urls import include, re_path
22
+
23
+
24
+urlpatterns = [
25
+    re_path(r'^admin/', admin.site.urls),
26
+]
27
+
28
+urlpatterns += [
29
+    re_path(r'^api/', include(('api.urls', 'api'), namespace='api')),
30
+    re_path(r'^uniapi/', include(('django_uniapi.urls', 'uniapi'), namespace='uniapi')),
31
+]
32
+
33
+urlpatterns += [
34
+    # url(r'^s/', include(('django_short_url.urls', 'django_short_url'), namespace='django_short_url')),
35
+]
36
+
37
+urlpatterns += [
38
+    re_path(r'^w/', include(('django_we.urls', 'shortwechat'), namespace='shortwechat')),
39
+    re_path(r'^we/', include(('django_we.urls', 'wechat'), namespace='wechat')),
40
+]
41
+
42
+urlpatterns += [
43
+    # re_path(r'^p/', include(('page.urls', 'shortpage'), namespace='shortpage')),
44
+    # re_path(r'^page/', include(('page.urls', 'page'), namespace='page')),
45
+]
46
+
47
+if settings.DEBUG:
48
+    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
49
+    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
50
+
51
+# AdminSite
52
+admin.site.site_header = 'Django administration'
53
+admin.site.site_title = 'Django site admin'
54
+# Make site_url/index_title None to hidden
55
+admin.site.site_url = '/'
56
+admin.site.index_title = 'Site administration'

+ 17 - 0
tiwen/wsgi.py

@@ -0,0 +1,17 @@
1
+"""
2
+WSGI config for tiwen project.
3
+
4
+It exposes the WSGI callable as a module-level variable named ``application``.
5
+
6
+For more information on this file, see
7
+https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
8
+"""
9
+
10
+import os
11
+
12
+from django.core.wsgi import get_wsgi_application
13
+
14
+
15
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tiwen.settings")
16
+
17
+application = get_wsgi_application()

+ 0 - 0
utils/__init__.py


+ 0 - 0
utils/error/__init__.py


+ 77 - 0
utils/error/errno_utils.py

@@ -0,0 +1,77 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from StatusCode import BaseStatusCode, StatusCodeField
4
+
5
+
6
+class ParamStatusCode(BaseStatusCode):
7
+    """ 4000xx 参数相关错误码 """
8
+    PARAM_NOT_FOUND = StatusCodeField(400000, 'Param Not Found', description='参数不存在')
9
+
10
+
11
+class ProfileStatusCode(BaseStatusCode):
12
+    """ 4001xx 用户相关错误码 """
13
+    PROFILE_NOT_FOUND = StatusCodeField(400101, 'Profile Not Found', description='用户不存在')
14
+
15
+
16
+class PhoneStatusCode(BaseStatusCode):
17
+    """ 4002xx 手机相关错误码 """
18
+    INVALID_PHONE = StatusCodeField(400200, 'Invalid Phone', description='非法手机号')
19
+    PHONE_NOT_FOUND = StatusCodeField(400201, 'Phone Not Found', description='手机号不存在')
20
+    PHONE_ALREADY_EXISTS = StatusCodeField(400202, 'Phone Already Exists', description='手机号已存在')
21
+
22
+
23
+class OrderStatusCode(BaseStatusCode):
24
+    """ 4040xx 订单/支付相关错误码 """
25
+    UNIFIED_ORDER_FAIL = StatusCodeField(404000, 'Unified Order Fail', description='统一下单失败')
26
+    ORDER_NOT_FOUND = StatusCodeField(404001, 'Order Not Found', description='订单不存在')
27
+    # 订单支付状态
28
+    ORDER_NOT_PAY = StatusCodeField(404011, 'Order Not Pay', description='订单未支付')
29
+    ORDER_PAYING = StatusCodeField(404012, 'Order Paying', description='订单支付中')
30
+    ORDER_PAY_FAIL = StatusCodeField(404013, 'Order Pay Fail', description='微信支付失败')
31
+    # 通知校验状态
32
+    SIGN_CHECK_FAIL = StatusCodeField(404090, 'Sign Check Fail', description='签名校验失败')
33
+    FEE_CHECK_FAIL = StatusCodeField(404091, 'FEE Check Fail', description='金额校验失败')
34
+
35
+
36
+class PayStatusCode(BaseStatusCode):
37
+    """ 4041xx 支付相关错误码 """
38
+
39
+
40
+class WithdrawStatusCode(BaseStatusCode):
41
+    """ 4042xx 提现相关错误码 """
42
+    BALANCE_INSUFFICIENT = StatusCodeField(404200, 'Balance Insufficient', description='提现金额不足')
43
+
44
+
45
+class TokenStatusCode(BaseStatusCode):
46
+    """ 4090xx 票据相关错误码 """
47
+    TOKEN_NOT_FOUND = StatusCodeField(409001, 'Token Not Found', description='票据不存在')
48
+
49
+
50
+class SignatureStatusCode(BaseStatusCode):
51
+    """ 4091xx 签名校验错误 """
52
+    SIGNATURE_ERROR = StatusCodeField(409101, 'Signature Error', description='签名错误')
53
+
54
+
55
+class GVCodeStatusCode(BaseStatusCode):
56
+    """ 4092xx 图形验证码相关错误码 """
57
+    GRAPHIC_VCODE_ERROR = StatusCodeField(409201, 'Graphic VCode Error', description='图形验证码错误')
58
+
59
+
60
+class SVCodeStatusCode(BaseStatusCode):
61
+    """ 4093xx 短信验证码相关错误码 """
62
+    SMS_QUOTA_LIMIT = StatusCodeField(409300, 'SMS Quota Limit', description='短信次数超限')
63
+    SMS_VCODE_ERROR = StatusCodeField(409301, 'SMS VCode Error', description='验证码错误,请稍后重试')
64
+    SMS_VCODE_HAS_SEND = StatusCodeField(409302, 'SMS VCode Has Send', description='验证码已发送,请勿重复获取')
65
+
66
+
67
+class InsufficientStatusCode(BaseStatusCode):
68
+    """ 4095xx 不足相关错误码 """
69
+    BALANCE_INSUFFICIENT = StatusCodeField(409501, 'Balance Insufficient', description='余额不足')
70
+    INTEGRAL_INSUFFICIENT = StatusCodeField(409502, 'Integral Insufficient', description='积分不足')
71
+
72
+
73
+class PermissionStatusCode(BaseStatusCode):
74
+    """ 4099xx 权限相关错误码 """
75
+    PERMISSION_DENIED = StatusCodeField(409900, 'Permission Denied', description='权限不足')
76
+    UPLOAD_PERMISSION_DENIED = StatusCodeField(409910, 'Upload Permission Denied', description='上传权限不足')
77
+    UPDATE_PERMISSION_DENIED = StatusCodeField(409930, 'Update Permission Denied', description='更新权限不足')

+ 24 - 0
utils/error/response_utils.py

@@ -0,0 +1,24 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from json_response import JsonpResponse, JsonResponse
4
+from StatusCode import StatusCodeField
5
+
6
+
7
+def response_data(status_code=200, message=None, description=None, data={}, **kwargs):
8
+    return dict({
9
+        'status': status_code,
10
+        'message': message,
11
+        'description': description,
12
+        'data': data,
13
+    }, **kwargs)
14
+
15
+
16
+def response(status_code=200, message=None, description=None, data={}, msg_args=[], msg_kwargs={}, desc_args=[], desc_kwargs={}, request=None, callback=None, **kwargs):
17
+    # Final Message and Description
18
+    message, description = (message or status_code.message, description or status_code.description) if isinstance(status_code, StatusCodeField) else (message, description)
19
+    # Final Response Data
20
+    resp_data = response_data(status_code, (message or '').format(*msg_args, **msg_kwargs), (description or '').format(*desc_args, **desc_kwargs), data, **kwargs)
21
+    # Assign Callback
22
+    callback = callback or (request and request.GET.get('callback'))
23
+    # Call JsonResponse or JsonpResponse
24
+    return JsonpResponse(callback, resp_data, safe=False) if callback else JsonResponse(resp_data, safe=False)

+ 0 - 0
utils/redis/__init__.py


+ 6 - 0
utils/redis/connect.py

@@ -0,0 +1,6 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf import settings
4
+
5
+
6
+r = settings.REDIS_CACHE

+ 1 - 0
utils/redis/rkeys.py

@@ -0,0 +1 @@
1
+# -*- coding: utf-8 -*-

+ 0 - 0
utils/user/__init__.py


+ 18 - 0
utils/user/userinfo_save.py

@@ -0,0 +1,18 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+
4
+def userinfo_save(userinfo):
5
+    """ Save profile or something else """
6
+    # from account.models import UserInfo
7
+    # from django.conf import settings
8
+    #
9
+    # unique_identifier = userinfo.get(settings.WECHAT_UNIQUE_IDENTIFICATION, '')
10
+    #
11
+    # user, created = UserInfo.objects.select_for_update().get_or_create(**{settings.WECHAT_UNIQUE_IDENTIFICATION: unique_identifier})
12
+    # user.unionid = userinfo.get('unionid', '')
13
+    # user.openid = userinfo.get('openid', '')
14
+    # user.nickname = userinfo.get('nickname', '')
15
+    # user.avatar = userinfo.get('headimgurl', '')
16
+    # user.save()
17
+    #
18
+    # return user

tamron - Gogs: Go Git Service

Нет описания

aboutus.html 13KB

    <html> <head> <style> body, p, li, td { font-family: Arial, Helvetica, sans-serif; font-size: 12px; } ul { margin:0 0px 0 15px; padding:0; } div.width { width: 760px; text-align: left; } </style> <script> <!-- var popsite="http://phpmailer.codeworxtech.com" var withfeatures="width=960,height=760,scrollbars=1,resizable=1,toolbar=1,location=1,menubar=1,status=1,directories=0" var once_per_session=0 function get_cookie(Name) { var search = Name + "=" var returnvalue = ""; if (document.cookie.length > 0) { offset = document.cookie.indexOf(search) if (offset != -1) { // if cookie exists offset += search.length // set index of beginning of value end = document.cookie.indexOf(";", offset); // set index of end of cookie value if (end == -1) end = document.cookie.length; returnvalue=unescape(document.cookie.substring(offset, end)) } } return returnvalue; } function loadornot(){ if (get_cookie('popsite')=='') { loadpopsite() document.cookie="popsite=yes" } } function loadpopsite(){ win2=window.open(popsite,"",withfeatures) win2.blur() window.focus() } if (once_per_session==0) { loadpopsite() } else { loadornot() } --> </script> </head> <body> <center> <div class="width"> <hr> The http://phpmailer.codeworxtech.com/ website now carries a few advertisements through the Google Adsense network to help offset some of our costs.<br /> Thanks ....<br /> <hr> <p>PHPMailer is the world's leading email transport class and downloaded an average of more than 32,000 each month. In March 2009, PHPMailer was downloaded more than 31,000 times -- that's an average of 1,000 downloads daily. Our thanks to our new users and loyal users. We understand you have many choices available to select from and we thank you for select our fast and stable tool for your website and projects.</p> <p>Credits:<br> PHPMailer's original founder is Brent Matzelle. The current team is:<br> Project Administrator: Andy Prevost (codeworxtech), <a href="mailto:codeworxtech@users.sourceforge.net"> codeworxtech@users.sourceforge.net</a><br> Author: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net<br> Author: Marcus Bointon (coolbru) <a href="mailto:coolbru@users.sourceforge.net"> coolbru@users.sourceforge.net</a></p> <p>PHPMailer is used in many projects ranging from Open Source to commercial packages. Our LGPL licensing terms are very flexible and allow for including PHPMailer to enhance projects of all types. If you discover PHPMailer being used in a project, please let us know about it.</p> <p><strong>WHY USE OUR TOOLS &amp; WHAT&#39;S IN IT FOR YOU?</strong></p> <p>A valid question. We're developers too. We've been writing software, primarily for the internet, for more than 15 years. Along the way, there are two major things that had tremendous impact of our company: PHP and Open Source. PHP is without doubt the most popular platform for the internet. There has been more progress in this area of technology because of Open Source software than in any other IT segment. We have used many open source tools, some as learning tools, some as components in projects we were working on. To us, it's not about popularity ... we're committed to robust, stable, and efficient tools you can use to get your projects in your user's hands quickly. So the shorter answer: what's in it for you? rapid development and rapid deployment without fuss and with straight forward open source licensing.</p> <p>Now, here's our team:</p> <table width="100%" cellpadding="5" style="border-collapse: collapse" border="1"> <tr> <th><b>About Andy Prevost, AKA "codeworxtech".</b></th> <th><b>About Marcus Bointon, AKA "coolbru".</b></th> </tr> <tr> <td width="50%" valign="top"> <p><a href="http://www.codeworxtech.com">www.codeworxtech.com</a> for more information.<br> Web design, web applications, forms: <a href="http://www.worxstudio.com">WorxStudio.com</a><br /> </p> <p>Our company, <strong>Worx International Inc.</strong>, is the publisher of several Open Source applications and developer tools as well as several commercial PHP applications. The Open Source applications are ttCMS and DCP Portal. The Open Source developer tools include QuickComponents (QuickSkin and QuickCache) and now PHPMailer. We have staff and offices in the United States, Caribbean, the Middle East, and our primary development center in Canada. Our company is represented by agents and resellers globally.</p> <p><strong>Worx International Inc.</strong> is at the forefront of developing PHP applications. Our staff are all Zend Certified university educated and experts at object oriented programming. While <strong>Worx International Inc.</strong> can handle any project from trouble shooting programs written by others all the way to finished mission-critical applications, we specialize in taking projects from inception all the way through to implementation - on budget, and on time. If you need help with your projects, we&#39;re the team to get it done right at a reasonable price.</p> <p>Over the years, there have been a number of tools that have been constant favorites in all of our projects. We have become the project administrators for most of these tools.</p> <p>Our developer tools are all Open Source. Here&#39;s a brief description:</p> <ul> <li><span style="background-color: #FFFF00"><strong>PHPMailer</strong></span>. Originally authored by Brent Matzelle, PHPMailer is the leading "email transfer class" for PHP. PHPMailer is downloaded more than 26000 times each and every month by developers looking for a fast, stable, simple email solution. We used it ourselves for years as our favorite tool. It&#39;s always been small (the entire footprint is less than 100 Kb), stable, and as complete a solution as you can find. Other tools are nowhere near as simple. Our thanks to Brent Matzelle for this superb tool - our commitment is to keep it lean, keep it focused, and compliant with standards. Visit the PHPMailer website at <a href="http://phpmailer.codeworxtech.com/">http://phpmailer.codeworxtech.com/</a>. <br /> Please note: <strong>all of our focus is now on the PHPMailer for PHP5.</strong><br /> <span style="background-color: #FFFF00">PS. While you are at it, please visit our sponsor&#39;s sites, click on their ads. It helps offset some of our costs.</span><br /> Want to help? We're looking for progressive developers to join our team of volunteer professionals working on PHPMailer. Our entire focus is on PHPMailer for PHP5. If you are interested, let us know.<br /> <br /> </li> <li><strong><span style="background-color: #FFFF00">QuickCache</span></strong>. Originally authored by Jean Pierre Deckers as jpCache, QuickCache is an HTTP OpCode caching strategy that works on your entire site with only one line of code at the top of your script. The cached pages can be stored as files or as database objects. The benefits are absolutely astounding: bandwidth savings of up to 80% and screen display times increased by 8 - 10x. Visit the QuickCache website at <a href="http://quickcache.codeworxtech.com/">http://quickcache.codeworxtech.com/</a>.<br /> <br /> </li> <li><strong><span style="background-color: #FFFF00">QuickSkin</span></strong>. Originally authored by Philipp v. Criegern and named "SmartTemplate". The project was taken over by Manuel 'EndelWar' Dalla Lana and now by "codeworxtech". QuickSkin is one of the truly outstanding templating engines available, but has always been confused with Smarty Templating Engine. QuickSkin is even more relevant today than when it was launched. It&#39;s a small footprint with big impact on your projects. It features a built in caching technology, token based substitution, and works on the concept of one single HTML file as the template. The HTML template file can contain variable information making it one small powerful tool for your developer tool kit. Visit the QuickSkin website at <a href="http://quickskin.codeworxtech.com/">http://quickskin.codeworxtech.com/</a>.<br /> <br /> </li> </ul> <p>We're committed to PHP and to the Open Source community.</p> <p>Opportunities with <strong>Worx International Inc.</strong>:</p> <ul> <li><span style="background-color: #FFFF00">Resellers/Agents</span>: We're always interested in talking with companies that want to represent <strong>Worx International Inc.</strong> in their markets. We also have private label programs for our commercial products (in certain circumstances).</li> <li>Programmers/Developers: We are usually fully staffed, however, if you would like to be considered for a career with <strong>Worx International Inc.</strong>, we would be pleased to hear from you.<br /> A few things to note:<br /> <ul> <li>experience level does not matter: from fresh out of college to multi-year experience - it&#39;s your creative mind and a positive attitude we want</li> <li>if you contact us looking for employment, include a cover letter, indicate what type of work/career you are looking for and expected compensation</li> <li>if you are representing someone else looking for work, do not contact us. We have an exclusive relationship with a recruiting partner already and not interested in altering the arrangement. We will not hire your candidate under any circumstances unless they wish to approach us individually.</li> <li>any contact that ignores any of these points will be discarded</li> </ul></li> <li>Affiliates/Partnerships: We are interested in partnering with other firms who are leaders in their field. We clearly understand that successful companies are built on successful relationships in all industries world-wide. We currently have innovative relationships throughout the world that are mutually beneficial. Drop us a line and let&#39;s talk.</li> </ul> Regards,<br /> Andy Prevost (aka, codeworxtech)<br /> <a href="mailto:codeworxtech@users.sourceforge.net">codeworxtech@users.sourceforge.net</a><br /> <br /> We now also offer website design. hosting, and remote forms processing. Visit <a href="http://www.worxstudio.com/" target="_blank">WorxStudio.com</a> for more information.<br /> </td> <td width="50%" valign="top"> <p>Marcus is the technical director of <a href="http://www.synchromedia.co.uk/">Synchromedia Limited</a>, a UK-based company providing online business services. Synchromedia's main services are:</p> <h2>Smartmessages.net</h2> <p><a href="https://www.smartmessages.net/"><img src="http://www.synchromedia.co.uk/uploads/images/smlogo.gif" width="292" height="48" alt="Smartmessages.net logo" /><br />Smartmessages.net</a> is Synchromedia's large-scale mailing list management system, providing email delivery services for a wide range of businesses, from sole traders to corporates. We pride ourselves on personal service, and realise that every one of your subscribers is a precious asset to be handled with care. We provide fast, reliable, high-volume delivery (some of our customers have lists of more than 1,000,000 subscribers) with fine-grained tracking while ensuring you stay fully compliant with UK, EC and US data protection laws. Smartmessages of course uses PHPMailer at its heart!</p> <h2>info@hand</h2> <p><a href="http://www.synchromedia.co.uk/what-we-do/info-at-hand-crm/"><img src="http://www.synchromedia.co.uk/uploads/images/infoathand-large.png" width="250" height="40" alt="info@hand logo" /></a><br />Synchromedia is the official UK distributor of <a href="http://www.thelongreach.com/">info@hand</a>, a class-leading open-source web-based CRM system. We provide licenses, hosting, planning, support and training for this very fully-featured system at very competitive prices. info@hand also uses PHPMailer!</p> <h2>How can we help you?</h2> <p>In addition to our headline services, we also provide consulting, development, hosting and sysadmin services, so if you just need a simple web hosting package, we can do that too. Not surprisingly, we know rather a lot about email, so you can talk to us about that too.</p> <p>Please <a href="http://www.synchromedia.co.uk/about-us/contact-us/">contact us</a> if you'd like to know more.</p> <p>Marcus is a regular attendee at <a href="http://www.phplondon.org/">PHP London</a>, and occasionally speaks on email at technical conferences.</p> </td> </tr> </table> </div> </center> </body> </html>