Ran 2to3 on pillar + some manual fixups

The 'manual fixups' are:

- incorrect use of dict.items() where dict.iteritems() was meant; this
  results in list(dict.items()), which I changed to dict.items().
- removal of 'from __future__ import' lines, which 2to3 changes into
  empty lines; I removed the empty lines.
This commit is contained in:
Sybren A. Stüvel 2017-03-03 12:00:24 +01:00
parent 10b3318419
commit 663627358f
39 changed files with 254 additions and 265 deletions

View File

@ -360,12 +360,12 @@ class PillarServer(Eve):
if node_type:
node_type = node_type.replace('_', ' ').title()
if doc_name:
description = u'%s "%s" was deleted.' % (node_type, doc_name)
description = '%s "%s" was deleted.' % (node_type, doc_name)
else:
description = u'This %s was deleted.' % (node_type, )
description = 'This %s was deleted.' % (node_type, )
else:
if doc_name:
description = u'"%s" was deleted.' % doc_name
description = '"%s" was deleted.' % doc_name
else:
description = None
@ -441,7 +441,7 @@ class PillarServer(Eve):
web.setup_app(self)
authentication.setup_app(self)
for ext in self.pillar_extensions.itervalues():
for ext in self.pillar_extensions.values():
self.log.info('Setting up extension %s', ext.name)
ext.setup_app(self)

View File

@ -18,7 +18,7 @@ log = logging.getLogger(__name__)
HOME_PROJECT_USERS = set()
# Users with any of these roles will get full write access to their home project.
HOME_PROJECT_WRITABLE_USERS = {u'subscriber', u'demo'}
HOME_PROJECT_WRITABLE_USERS = {'subscriber', 'demo'}
HOME_PROJECT_DESCRIPTION = ('# Your home project\n\n'
'This is your home project. It allows synchronisation '
@ -30,7 +30,7 @@ HOME_PROJECT_SUMMARY = 'This is your home project. Here you can sync your Blende
# 'as a pastebin for text, images and other assets, and '
# 'allows synchronisation of your Blender settings.')
# HOME_PROJECT_SUMMARY = 'This is your home project. Pastebin and Blender settings sync in one!'
SYNC_GROUP_NODE_NAME = u'Blender Sync'
SYNC_GROUP_NODE_NAME = 'Blender Sync'
SYNC_GROUP_NODE_DESC = ('The [Blender Cloud Addon](https://cloud.blender.org/services'
'#blender-addon) will synchronize your Blender settings here.')
@ -135,8 +135,8 @@ def create_home_project(user_id, write_access):
# This allows people to comment on shared images and see comments.
node_type_comment = assign_permissions(
node_type_comment,
subscriber_methods=[u'GET', u'POST'],
world_methods=[u'GET'])
subscriber_methods=['GET', 'POST'],
world_methods=['GET'])
project['node_types'] = [
node_type_group,
@ -215,7 +215,7 @@ def home_project():
write_access = write_access_with_roles(roles)
create_home_project(user_id, write_access)
resp, _, _, status, _ = get('projects', category=u'home', user=user_id)
resp, _, _, status, _ = get('projects', category='home', user=user_id)
if status != 200:
return utils.jsonify(resp), status
@ -248,8 +248,8 @@ def home_project_permissions(write_access):
"""
if write_access:
return [u'GET', u'PUT', u'POST', u'DELETE']
return [u'GET']
return ['GET', 'PUT', 'POST', 'DELETE']
return ['GET']
def has_home_project(user_id):

View File

@ -86,7 +86,7 @@ def upsert_user(db_user, blender_id_user_id):
:type: (ObjectId, int)
"""
if u'subscriber' in db_user.get('groups', []):
if 'subscriber' in db_user.get('groups', []):
log.error('Non-ObjectID string found in user.groups: %s', db_user)
raise wz_exceptions.InternalServerError('Non-ObjectID string found in user.groups: %s' % db_user)
@ -117,8 +117,8 @@ def upsert_user(db_user, blender_id_user_id):
if status == 422:
# Probably non-unique username, so retry a few times with different usernames.
log.info('Error creating new user: %s', r)
username_issue = r.get('_issues', {}).get(u'username', '')
if u'not unique' in username_issue:
username_issue = r.get('_issues', {}).get('username', '')
if 'not unique' in username_issue:
# Retry
db_user['username'] = authentication.make_unique_username(db_user['email'])
continue

View File

@ -61,13 +61,13 @@ class ValidateCustomFields(Validator):
Only validates the dict values, not the keys. Modifies the given dict in-place.
"""
assert dict_valueschema[u'type'] == u'dict'
assert dict_valueschema['type'] == 'dict'
assert isinstance(dict_property, dict)
for key, val in dict_property.items():
item_schema = {u'item': dict_valueschema}
item_prop = {u'item': val}
dict_property[key] = self.convert_properties(item_prop, item_schema)[u'item']
item_schema = {'item': dict_valueschema}
item_prop = {'item': val}
dict_property[key] = self.convert_properties(item_prop, item_schema)['item']
def _validate_valid_properties(self, valid_properties, field, value):
from pillar.api.utils import project_get_node_type

View File

@ -723,7 +723,7 @@ users = {
# By default don't include the 'auth' field. It can still be obtained
# using projections, though, so we block that in hooks.
'datasource': {'projection': {u'auth': 0}},
'datasource': {'projection': {'auth': 0}},
'schema': users_schema
}

View File

@ -222,7 +222,7 @@ def process_file(gcs, file_id, local_file):
mime_category, src_file['format'] = src_file['content_type'].split('/', 1)
# Prevent video handling for non-admins.
if not user_has_role(u'admin') and mime_category == 'video':
if not user_has_role('admin') and mime_category == 'video':
if src_file['format'].startswith('x-'):
xified = src_file['format']
else:

View File

@ -29,7 +29,7 @@ def change_file_storage_backend(file_id, dest_backend):
Files on the original backend are not deleted automatically.
"""
dest_backend = unicode(dest_backend)
dest_backend = str(dest_backend)
file_id = ObjectId(file_id)
# Fetch file document

View File

@ -87,7 +87,7 @@ def generate_and_store_token(user_id, days=15, prefix=''):
def hash_password(password, salt):
if isinstance(salt, unicode):
if isinstance(salt, str):
salt = salt.encode('utf-8')
encoded_password = base64.b64encode(hashlib.sha256(password).digest())
return bcrypt.hashpw(encoded_password, salt)

View File

@ -1,7 +1,7 @@
import base64
import functools
import logging
import urlparse
import urllib.parse
import pymongo.errors
import rsa.randnum
@ -20,7 +20,7 @@ from pillar.api.utils.gcs import update_file_name
log = logging.getLogger(__name__)
blueprint = Blueprint('nodes_api', __name__)
ROLES_FOR_SHARING = {u'subscriber', u'demo'}
ROLES_FOR_SHARING = {'subscriber', 'demo'}
def only_for_node_type_decorator(*required_node_type_names):
@ -138,7 +138,7 @@ def make_world_gettable(node):
log.debug('Ensuring the world can read node %s', node_id)
world_perms = set(node.get('permissions', {}).get('world', []))
world_perms.add(u'GET')
world_perms.add('GET')
world_perms = list(world_perms)
result = nodes_coll.update_one({'_id': node_id},
@ -164,7 +164,7 @@ def create_short_code(node):
def short_link_info(short_code):
"""Returns the short link info in a dict."""
short_link = urlparse.urljoin(current_app.config['SHORT_LINK_BASE_URL'], short_code)
short_link = urllib.parse.urljoin(current_app.config['SHORT_LINK_BASE_URL'], short_code)
return {
'short_code': short_code,
@ -349,7 +349,7 @@ def node_set_default_picture(node, original=None):
# Find the colour map, defaulting to the first image map available.
image_file_id = None
for image in props.get('files', []):
if image_file_id is None or image.get('map_type') == u'color':
if image_file_id is None or image.get('map_type') == 'color':
image_file_id = image.get('file')
else:
log.debug('Not setting default picture on node type %s content type %s',

View File

@ -11,20 +11,20 @@ from pillar.api.utils import authorization, authentication, jsonify
from . import register_patch_handler
log = logging.getLogger(__name__)
ROLES_FOR_COMMENT_VOTING = {u'subscriber', u'demo'}
COMMENT_VOTING_OPS = {u'upvote', u'downvote', u'revoke'}
VALID_COMMENT_OPERATIONS = COMMENT_VOTING_OPS.union({u'edit'})
ROLES_FOR_COMMENT_VOTING = {'subscriber', 'demo'}
COMMENT_VOTING_OPS = {'upvote', 'downvote', 'revoke'}
VALID_COMMENT_OPERATIONS = COMMENT_VOTING_OPS.union({'edit'})
@register_patch_handler(u'comment')
@register_patch_handler('comment')
def patch_comment(node_id, patch):
assert_is_valid_patch(node_id, patch)
user_id = authentication.current_user_id()
if patch[u'op'] in COMMENT_VOTING_OPS:
if patch['op'] in COMMENT_VOTING_OPS:
result, node = vote_comment(user_id, node_id, patch)
else:
assert patch[u'op'] == u'edit', 'Invalid patch operation %s' % patch[u'op']
assert patch['op'] == 'edit', 'Invalid patch operation %s' % patch['op']
result, node = edit_comment(user_id, node_id, patch)
return jsonify({'_status': 'OK',
@ -95,9 +95,9 @@ def vote_comment(user_id, node_id, patch):
return update
actions = {
u'upvote': upvote,
u'downvote': downvote,
u'revoke': revoke,
'upvote': upvote,
'downvote': downvote,
'revoke': revoke,
}
action = actions[patch['op']]
mongo_update = action()
@ -141,7 +141,7 @@ def edit_comment(user_id, node_id, patch):
log.warning('User %s wanted to patch non-existing node %s' % (user_id, node_id))
raise wz_exceptions.NotFound('Node %s not found' % node_id)
if node['user'] != user_id and not authorization.user_has_role(u'admin'):
if node['user'] != user_id and not authorization.user_has_role('admin'):
raise wz_exceptions.Forbidden('You can only edit your own comments.')
# Use Eve to PATCH this node, as that also updates the etag.
@ -173,8 +173,8 @@ def assert_is_valid_patch(node_id, patch):
raise wz_exceptions.BadRequest("PATCH should have a key 'op' indicating the operation.")
if op not in VALID_COMMENT_OPERATIONS:
raise wz_exceptions.BadRequest(u'Operation should be one of %s',
u', '.join(VALID_COMMENT_OPERATIONS))
raise wz_exceptions.BadRequest('Operation should be one of %s',
', '.join(VALID_COMMENT_OPERATIONS))
if op not in COMMENT_VOTING_OPS:
# We can't check here, we need the node owner for that.

View File

@ -28,7 +28,7 @@ def before_inserting_projects(items):
"""
# Allow admin users to do whatever they want.
if user_has_role(u'admin'):
if user_has_role('admin'):
return
for item in items:
@ -70,7 +70,7 @@ def protect_sensitive_fields(document, original):
"""When not logged in as admin, prevents update to certain fields."""
# Allow admin users to do whatever they want.
if user_has_role(u'admin'):
if user_has_role('admin'):
return
def revert(name):

View File

@ -16,7 +16,7 @@ blueprint_api = Blueprint('projects_api', __name__)
@blueprint_api.route('/create', methods=['POST'])
@authorization.require_login(require_roles={u'admin', u'subscriber', u'demo'})
@authorization.require_login(require_roles={'admin', 'subscriber', 'demo'})
def create_project(overrides=None):
"""Creates a new project."""
@ -65,7 +65,7 @@ def project_manage_users():
project = projects_collection.find_one({'_id': project_id})
# Check if the current_user is owner of the project, or removing themselves.
if not authorization.user_has_role(u'admin'):
if not authorization.user_has_role('admin'):
remove_self = target_user_id == current_user_id and action == 'remove'
if project['user'] != current_user_id and not remove_self:
utils.abort_with_error(403)

View File

@ -13,7 +13,7 @@ blueprint = Blueprint('service', __name__)
log = logging.getLogger(__name__)
signal_user_changed_role = blinker.NamedSignal('badger:user_changed_role')
ROLES_WITH_GROUPS = {u'admin', u'demo', u'subscriber'}
ROLES_WITH_GROUPS = {'admin', 'demo', 'subscriber'}
# Map of role name to group ID, for the above groups.
role_to_group_id = {}
@ -38,7 +38,7 @@ def fetch_role_to_group_id_map():
@blueprint.route('/badger', methods=['POST'])
@authorization.require_login(require_roles={u'service', u'badger'}, require_all=True)
@authorization.require_login(require_roles={'service', 'badger'}, require_all=True)
def badger():
if request.mimetype != 'application/json':
log.debug('Received %s instead of application/json', request.mimetype)
@ -117,7 +117,7 @@ def do_badger(action, user_email, role):
@blueprint.route('/urler/<project_id>', methods=['GET'])
@authorization.require_login(require_roles={u'service', u'urler'}, require_all=True)
@authorization.require_login(require_roles={'service', 'urler'}, require_all=True)
def urler(project_id):
"""Returns the URL of any project."""
@ -189,7 +189,7 @@ def create_service_account(email, roles, service, update_existing=None):
raise ValueError('User %s already exists' % email)
# Compute the new roles, and assign.
roles = list(set(roles).union({u'service'}).union(user['roles']))
roles = list(set(roles).union({'service'}).union(user['roles']))
user['roles'] = list(roles)
# Let the caller perform any required updates.
@ -204,7 +204,7 @@ def create_service_account(email, roles, service, update_existing=None):
expected_status = 200
else:
# Create a user with the correct roles.
roles = list(set(roles).union({u'service'}))
roles = list(set(roles).union({'service'}))
user = {'username': email,
'groups': [],
'roles': roles,

View File

@ -60,7 +60,7 @@ def check_user_access(request, lookup):
current_user_id = current_user['user_id'] if current_user else None
# Admins can do anything and get everything, except the 'auth' block.
if user_has_role(u'admin'):
if user_has_role('admin'):
return
if not lookup and not current_user:
@ -74,7 +74,7 @@ def check_user_access(request, lookup):
def check_put_access(request, lookup):
"""Only allow PUT to the current user, or all users if admin."""
if user_has_role(u'admin'):
if user_has_role('admin'):
return
current_user = g.get('current_user')
@ -94,7 +94,7 @@ def after_fetching_user(user):
current_user_id = current_user['user_id'] if current_user else None
# Admins can do anything and get everything, except the 'auth' block.
if user_has_role(u'admin'):
if user_has_role('admin'):
return
# Only allow full access to the current user.

View File

@ -1,7 +1,7 @@
import copy
import hashlib
import json
import urllib
import urllib.request, urllib.parse, urllib.error
import datetime
import functools
@ -103,7 +103,7 @@ def skip_when_testing(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if current_app.config['TESTING']:
log.debug('Skipping call to %s(...) due to TESTING', func.func_name)
log.debug('Skipping call to %s(...) due to TESTING', func.__name__)
return None
return func(*args, **kwargs)
@ -145,19 +145,18 @@ def gravatar(email, size=64):
parameters = {'s': str(size), 'd': 'mm'}
return "https://www.gravatar.com/avatar/" + \
hashlib.md5(str(email)).hexdigest() + \
"?" + urllib.urlencode(parameters)
"?" + urllib.parse.urlencode(parameters)
class MetaFalsey(type):
def __nonzero__(cls):
def __bool__(cls):
return False
__bool__ = __nonzero__ # for Python 3
class DoesNotExist(object):
class DoesNotExist(object, metaclass=MetaFalsey):
"""Returned as value by doc_diff if a value does not exist."""
__metaclass__ = MetaFalsey
def doc_diff(doc1, doc2, falsey_is_equal=True):
@ -175,7 +174,7 @@ def doc_diff(doc1, doc2, falsey_is_equal=True):
"""
for key in set(doc1.keys()).union(set(doc2.keys())):
if isinstance(key, basestring) and key[0] == u'_':
if isinstance(key, str) and key[0] == '_':
continue
val1 = doc1.get(key, DoesNotExist)

View File

@ -124,7 +124,7 @@ def store_token(user_id, token, token_expiry, oauth_subclient_id=False):
:returns: the token document from MongoDB
"""
assert isinstance(token, (str, unicode)), 'token must be string type, not %r' % type(token)
assert isinstance(token, str), 'token must be string type, not %r' % type(token)
token_data = {
'user': user_id,

View File

@ -238,22 +238,22 @@ def merge_permissions(*args):
asdict0 = {permission[field_name]: permission['methods'] for permission in from0}
asdict1 = {permission[field_name]: permission['methods'] for permission in from1}
keys = set(asdict0.keys() + asdict1.keys())
keys = set(asdict0.keys()).union(set(asdict1.keys()))
for key in maybe_sorted(keys):
methods0 = asdict0.get(key, [])
methods1 = asdict1.get(key, [])
methods = maybe_sorted(set(methods0).union(set(methods1)))
effective.setdefault(plural_name, []).append({field_name: key, u'methods': methods})
effective.setdefault(plural_name, []).append({field_name: key, 'methods': methods})
merge(u'user')
merge(u'group')
merge('user')
merge('group')
# Gather permissions for world
world0 = args[0].get('world', [])
world1 = args[1].get('world', [])
world_methods = set(world0).union(set(world1))
if world_methods:
effective[u'world'] = maybe_sorted(world_methods)
effective['world'] = maybe_sorted(world_methods)
# Recurse for longer merges
if len(args) > 2:
@ -380,4 +380,4 @@ def user_matches_roles(require_roles=set(),
def is_admin(user):
"""Returns True iff the given user has the admin role."""
return user_has_role(u'admin', user)
return user_has_role('admin', user)

View File

@ -173,7 +173,7 @@ class GoogleCloudStorageBucket(Bucket):
"""Set the ContentDisposition metadata so that when a file is downloaded
it has a human-readable name.
"""
blob.content_disposition = u'attachment; filename="{0}"'.format(name)
blob.content_disposition = 'attachment; filename="{0}"'.format(name)
blob.patch()
def copy_blob(self, blob, to_bucket):
@ -215,11 +215,11 @@ def update_file_name(node):
if node['properties'].get('status', '') == 'processing':
return
def _format_name(name, override_ext, size=None, map_type=u''):
def _format_name(name, override_ext, size=None, map_type=''):
root, _ = os.path.splitext(name)
size = u'-{}'.format(size) if size else u''
map_type = u'-{}'.format(map_type) if map_type else u''
return u'{}{}{}{}'.format(root, size, map_type, override_ext)
size = '-{}'.format(size) if size else ''
map_type = '-{}'.format(map_type) if map_type else ''
return '{}{}{}{}'.format(root, size, map_type, override_ext)
def _update_name(file_id, file_props):
files_collection = current_app.data.driver.db['files']
@ -229,7 +229,7 @@ def update_file_name(node):
return
# For textures -- the map type should be part of the name.
map_type = file_props.get('map_type', u'')
map_type = file_props.get('map_type', '')
storage = GoogleCloudStorageBucket(str(node['project']))
blob = storage.Get(file_doc['file_path'], to_dict=False)

View File

@ -21,7 +21,7 @@ def generate_local_thumbnails(name_base, src):
save_to_base, _ = os.path.splitext(src)
name_base, _ = os.path.splitext(name_base)
for size, settings in thumbnail_settings.iteritems():
for size, settings in thumbnail_settings.items():
dst = '{0}-{1}{2}'.format(save_to_base, size, '.jpg')
name = '{0}-{1}{2}'.format(name_base, size, '.jpg')
@ -143,7 +143,7 @@ def get_video_data(filepath):
res_y=video_stream['height'],
)
if video_stream['sample_aspect_ratio'] != '1:1':
print '[warning] Pixel aspect ratio is not square!'
print('[warning] Pixel aspect ratio is not square!')
return outdata
@ -190,14 +190,14 @@ def ffmpeg_encode(src, format, res_y=720):
dst = os.path.splitext(src)
dst = "{0}-{1}p.{2}".format(dst[0], res_y, format)
args.append(dst)
print "Encoding {0} to {1}".format(src, format)
print("Encoding {0} to {1}".format(src, format))
returncode = subprocess.call([current_app.config['BIN_FFMPEG']] + args)
if returncode == 0:
print "Successfully encoded {0}".format(dst)
print("Successfully encoded {0}".format(dst))
else:
print "Error during encode"
print "Code: {0}".format(returncode)
print "Command: {0}".format(current_app.config['BIN_FFMPEG'] + " " + " ".join(args))
print("Error during encode")
print("Code: {0}".format(returncode))
print("Command: {0}".format(current_app.config['BIN_FFMPEG'] + " " + " ".join(args)))
dst = None
# return path of the encoded video
return dst

View File

@ -55,11 +55,11 @@ def _load_user(token):
login_user = UserClass(token)
login_user.email = db_user['email']
login_user.objectid = unicode(db_user['_id'])
login_user.objectid = str(db_user['_id'])
login_user.username = db_user['username']
login_user.gravatar = utils.gravatar(db_user['email'])
login_user.roles = db_user.get('roles', [])
login_user.groups = [unicode(g) for g in db_user['groups'] or ()]
login_user.groups = [str(g) for g in db_user['groups'] or ()]
login_user.full_name = db_user.get('full_name', '')
return login_user

View File

@ -3,7 +3,7 @@
Run commands with 'flask <command>'
"""
from __future__ import print_function, division
import copy
import logging
@ -57,7 +57,7 @@ def setup_db(admin_email):
print("Created user {0}".format(user['_id']))
# Create a default project by faking a POST request.
with current_app.test_request_context(data={'project_name': u'Default Project'}):
with current_app.test_request_context(data={'project_name': 'Default Project'}):
from flask import g
from pillar.api.projects import routes as proj_routes
@ -89,7 +89,7 @@ def find_duplicate_users():
blender_id = blender_ids[0]
found_users[blender_id].append(user)
for blender_id, users in found_users.iteritems():
for blender_id, users in found_users.items():
if len(users) == 1:
continue
@ -344,14 +344,14 @@ def create_badger_account(email, badges):
this account can assign and revoke.
"""
create_service_account(email, [u'badger'], {'badger': badges.strip().split()})
create_service_account(email, ['badger'], {'badger': badges.strip().split()})
@manager_setup.command
def create_urler_account(email):
"""Creates a new service account that can fetch all project URLs."""
create_service_account(email, [u'urler'], {})
create_service_account(email, ['urler'], {})
@manager_setup.command
@ -704,7 +704,7 @@ def upgrade_attachment_schema(proj_url=None, all_projects=False):
'properties.attachments': {'$exists': True},
})
for node in nodes:
attachments = node[u'properties'][u'attachments']
attachments = node['properties']['attachments']
if isinstance(attachments, dict):
# This node has already been upgraded.
continue
@ -713,9 +713,9 @@ def upgrade_attachment_schema(proj_url=None, all_projects=False):
new_atts = {}
for field_info in attachments:
for attachment in field_info.get('files', []):
new_atts[attachment[u'slug']] = {u'oid': attachment[u'file']}
new_atts[attachment['slug']] = {'oid': attachment['file']}
node[u'properties'][u'attachments'] = new_atts
node['properties']['attachments'] = new_atts
# Use Eve to PUT, so we have schema checking.
db_node = remove_private_keys(node)
@ -774,11 +774,11 @@ def create_blog(proj_url):
blog = nodes_coll.find_one({'node_type': 'blog', 'project': proj_id})
if not blog:
blog = {
u'node_type': node_type_blog['name'],
u'name': u'Blog',
u'description': u'',
u'properties': {},
u'project': proj_id,
'node_type': node_type_blog['name'],
'name': 'Blog',
'description': '',
'properties': {},
'project': proj_id,
}
r, _, _, status = post_internal('nodes', blog)
if status != 201:

View File

@ -81,7 +81,7 @@ FILE_LINK_VALIDITY = defaultdict(
)
# Roles with full GET-access to all variations of files.
FULL_FILE_ACCESS_ROLES = {u'admin', u'subscriber', u'demo'}
FULL_FILE_ACCESS_ROLES = {'admin', 'subscriber', 'demo'}
# Client and Subclient IDs for Blender ID
BLENDER_ID_CLIENT_ID = 'SPECIAL-SNOWFLAKE-57'
@ -119,7 +119,7 @@ SHORT_CODE_LENGTH = 6 # characters
# People are allowed this many bytes per uploaded file.
FILESIZE_LIMIT_BYTES_NONSUBS = 32 * 2 ** 20
# Unless they have one of those roles.
ROLES_FOR_UNLIMITED_UPLOADS = {u'subscriber', u'demo', u'admin'}
ROLES_FOR_UNLIMITED_UPLOADS = {'subscriber', 'demo', 'admin'}
#############################################

View File

@ -18,9 +18,7 @@ can then be registered to the application at app creation time:
import abc
class PillarExtension(object):
__metaclass__ = abc.ABCMeta
class PillarExtension(object, metaclass=abc.ABCMeta):
@abc.abstractproperty
def name(self):
"""The name of this extension.

View File

@ -3,8 +3,6 @@
This is for user-generated stuff, like comments.
"""
from __future__ import absolute_import
import bleach
import CommonMark

View File

@ -1,7 +1,7 @@
"""PillarSDK subclass for direct Flask-internal calls."""
import logging
import urlparse
import urllib.parse
from flask import current_app
import pillarsdk
@ -23,8 +23,8 @@ class FlaskInternalApi(pillarsdk.Api):
self.requests_to_flask_kwargs(kwargs)
# Leave out the query string and fragment from the URL.
split_url = urlparse.urlsplit(url)
path = urlparse.urlunsplit(split_url[:-2] + (None, None))
split_url = urllib.parse.urlsplit(url)
path = urllib.parse.urlunsplit(split_url[:-2] + (None, None))
try:
response = client.open(path=path, query_string=split_url.query, method=method,
**kwargs)

View File

@ -1,9 +1,5 @@
# -*- encoding: utf-8 -*-
from __future__ import print_function
from __future__ import absolute_import
import base64
import copy
import json
@ -16,7 +12,7 @@ import sys
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencode
from urllib.parse import urlencode
from bson import ObjectId, tz_util
@ -46,7 +42,7 @@ MY_PATH = os.path.dirname(os.path.abspath(__file__))
TEST_EMAIL_USER = 'koro'
TEST_EMAIL_ADDRESS = '%s@testing.blender.org' % TEST_EMAIL_USER
TEST_FULL_NAME = u'врач Сергей'
TEST_FULL_NAME = 'врач Сергей'
TEST_SUBCLIENT_TOKEN = 'my-subclient-token-for-pillar'
BLENDER_ID_USER_RESPONSE = {'status': 'success',
'user': {'email': TEST_EMAIL_ADDRESS,
@ -207,9 +203,9 @@ class AbstractPillarTest(TestMinimal):
'roles': list(roles),
'settings': {'email_communications': 1},
'auth': [{'token': '',
'user_id': unicode(ctd.BLENDER_ID_TEST_USERID),
'user_id': str(ctd.BLENDER_ID_TEST_USERID),
'provider': 'blender-id'}],
'full_name': u'คนรักของผัดไทย',
'full_name': 'คนรักของผัดไทย',
'email': TEST_EMAIL_ADDRESS
})

View File

@ -11,55 +11,55 @@ EXAMPLE_PROJECT_READONLY_GROUP2_ID = ObjectId('564733b56dcaf85da2faee8a')
EXAMPLE_PROJECT_ID = ObjectId('5672beecc0261b2005ed1a33')
EXAMPLE_PROJECT_OWNER_ID = ObjectId('552b066b41acdf5dec4436f2')
EXAMPLE_FILE = {u'_id': ObjectId('5672e2c1c379cf0007b31995'),
u'_updated': datetime.datetime(2016, 3, 25, 10, 28, 24, tzinfo=tz_util.utc),
u'height': 2048,
u'name': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png', u'format': 'png',
u'variations': [
{u'format': 'jpg', u'height': 160, u'width': 160, u'length': 8558,
u'link': 'http://localhost:8002/file-variant-h', u'content_type': 'image/jpeg',
u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-b.jpg',
u'size': 'b'},
{u'format': 'jpg', u'height': 2048, u'width': 2048, u'length': 819569,
u'link': 'http://localhost:8002/file-variant-h', u'content_type': 'image/jpeg',
u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-h.jpg',
u'size': 'h'},
{u'format': 'jpg', u'height': 64, u'width': 64, u'length': 8195,
u'link': 'http://localhost:8002/file-variant-t', u'content_type': 'image/jpeg',
u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-t.jpg',
u'size': 't'},
EXAMPLE_FILE = {'_id': ObjectId('5672e2c1c379cf0007b31995'),
'_updated': datetime.datetime(2016, 3, 25, 10, 28, 24, tzinfo=tz_util.utc),
'height': 2048,
'name': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png', 'format': 'png',
'variations': [
{'format': 'jpg', 'height': 160, 'width': 160, 'length': 8558,
'link': 'http://localhost:8002/file-variant-h', 'content_type': 'image/jpeg',
'md5': '--', 'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-b.jpg',
'size': 'b'},
{'format': 'jpg', 'height': 2048, 'width': 2048, 'length': 819569,
'link': 'http://localhost:8002/file-variant-h', 'content_type': 'image/jpeg',
'md5': '--', 'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-h.jpg',
'size': 'h'},
{'format': 'jpg', 'height': 64, 'width': 64, 'length': 8195,
'link': 'http://localhost:8002/file-variant-t', 'content_type': 'image/jpeg',
'md5': '--', 'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-t.jpg',
'size': 't'},
],
u'filename': 'brick_dutch_soft_bump.png',
u'project': EXAMPLE_PROJECT_ID,
u'width': 2048, u'length': 6227670,
u'user': ObjectId('56264fc4fa3a250344bd10c5'),
u'content_type': 'image/png',
u'_etag': '044ce3aede2e123e261c0d8bd77212f264d4f7b0',
u'_created': datetime.datetime(2015, 12, 17, 16, 28, 49, tzinfo=tz_util.utc),
u'md5': '',
u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png',
u'backend': 'pillar',
u'link': 'http://localhost:8002/file',
u'link_expires': datetime.datetime(2016, 3, 22, 9, 28, 22, tzinfo=tz_util.utc)}
'filename': 'brick_dutch_soft_bump.png',
'project': EXAMPLE_PROJECT_ID,
'width': 2048, 'length': 6227670,
'user': ObjectId('56264fc4fa3a250344bd10c5'),
'content_type': 'image/png',
'_etag': '044ce3aede2e123e261c0d8bd77212f264d4f7b0',
'_created': datetime.datetime(2015, 12, 17, 16, 28, 49, tzinfo=tz_util.utc),
'md5': '',
'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png',
'backend': 'pillar',
'link': 'http://localhost:8002/file',
'link_expires': datetime.datetime(2016, 3, 22, 9, 28, 22, tzinfo=tz_util.utc)}
EXAMPLE_PROJECT = {
u'_created': datetime.datetime(2015, 12, 17, 13, 22, 56, tzinfo=tz_util.utc),
u'_etag': u'cc4643e98d3606f87bbfaaa200bfbae941b642f3',
u'_id': EXAMPLE_PROJECT_ID,
u'_updated': datetime.datetime(2016, 1, 7, 18, 59, 4, tzinfo=tz_util.utc),
u'category': u'assets',
u'description': u'Welcome to this curated collection of Blender Institute textures and image '
u'resources. This collection is an on-going project, as with each project we '
u'create a number of textures based on our own resources (photographs, scans, '
u'etc.) or made completely from scratch. At the moment you can find all the '
u'textures from the past Open Projects that were deemed re-usable. \r\n\r\n'
u'People who have contributed to these textures:\r\n\r\nAndrea Weikert, Andy '
u'Goralczyk, Basse Salmela, Ben Dansie, Campbell Barton, Enrico Valenza, Ian '
u'Hubert, Kjartan Tysdal, Manu J\xe4rvinen, Massimiliana Pulieso, Matt Ebb, '
u'Pablo Vazquez, Rob Tuytel, Roland Hess, Sarah Feldlaufer, S\xf6nke M\xe4ter',
u'is_private': False,
u'name': u'Unittest project',
u'node_types': [
'_created': datetime.datetime(2015, 12, 17, 13, 22, 56, tzinfo=tz_util.utc),
'_etag': 'cc4643e98d3606f87bbfaaa200bfbae941b642f3',
'_id': EXAMPLE_PROJECT_ID,
'_updated': datetime.datetime(2016, 1, 7, 18, 59, 4, tzinfo=tz_util.utc),
'category': 'assets',
'description': 'Welcome to this curated collection of Blender Institute textures and image '
'resources. This collection is an on-going project, as with each project we '
'create a number of textures based on our own resources (photographs, scans, '
'etc.) or made completely from scratch. At the moment you can find all the '
'textures from the past Open Projects that were deemed re-usable. \r\n\r\n'
'People who have contributed to these textures:\r\n\r\nAndrea Weikert, Andy '
'Goralczyk, Basse Salmela, Ben Dansie, Campbell Barton, Enrico Valenza, Ian '
'Hubert, Kjartan Tysdal, Manu J\xe4rvinen, Massimiliana Pulieso, Matt Ebb, '
'Pablo Vazquez, Rob Tuytel, Roland Hess, Sarah Feldlaufer, S\xf6nke M\xe4ter',
'is_private': False,
'name': 'Unittest project',
'node_types': [
PILLAR_NAMED_NODE_TYPES['group_texture'],
PILLAR_NAMED_NODE_TYPES['group'],
PILLAR_NAMED_NODE_TYPES['asset'],
@ -69,36 +69,36 @@ EXAMPLE_PROJECT = {
PILLAR_NAMED_NODE_TYPES['post'],
PILLAR_NAMED_NODE_TYPES['texture'],
],
u'nodes_blog': [],
u'nodes_featured': [],
u'nodes_latest': [],
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'POST', u'PUT', u'DELETE']}],
u'users': [],
u'world': [u'GET']},
u'picture_header': ObjectId('5673f260c379cf0007b31bc4'),
u'picture_square': ObjectId('5673f256c379cf0007b31bc3'),
u'status': u'published',
u'summary': u'Texture collection from all Blender Institute open projects.',
u'url': u'textures',
u'user': EXAMPLE_PROJECT_OWNER_ID}
'nodes_blog': [],
'nodes_featured': [],
'nodes_latest': [],
'permissions': {'groups': [{'group': EXAMPLE_ADMIN_GROUP_ID,
'methods': ['GET', 'POST', 'PUT', 'DELETE']}],
'users': [],
'world': ['GET']},
'picture_header': ObjectId('5673f260c379cf0007b31bc4'),
'picture_square': ObjectId('5673f256c379cf0007b31bc3'),
'status': 'published',
'summary': 'Texture collection from all Blender Institute open projects.',
'url': 'textures',
'user': EXAMPLE_PROJECT_OWNER_ID}
EXAMPLE_NODE = {
u'_id': ObjectId('572761099837730efe8e120d'),
u'picture': ObjectId('572761f39837730efe8e1210'),
u'description': u'',
u'node_type': u'asset',
u'user': ObjectId('57164ca1983773118cbaf779'),
u'properties': {
u'status': u'published',
u'content_type': u'image',
u'file': ObjectId('572761129837730efe8e120e')
'_id': ObjectId('572761099837730efe8e120d'),
'picture': ObjectId('572761f39837730efe8e1210'),
'description': '',
'node_type': 'asset',
'user': ObjectId('57164ca1983773118cbaf779'),
'properties': {
'status': 'published',
'content_type': 'image',
'file': ObjectId('572761129837730efe8e120e')
},
u'_updated': datetime.datetime(2016, 5, 2, 14, 19, 58, 0, tzinfo=tz_util.utc),
u'name': u'Image test',
u'project': EXAMPLE_PROJECT_ID,
u'_created': datetime.datetime(2016, 5, 2, 14, 19, 37, 0, tzinfo=tz_util.utc),
u'_etag': u'6b8589b42c880e3626f43f3e82a5c5b946742687'
'_updated': datetime.datetime(2016, 5, 2, 14, 19, 58, 0, tzinfo=tz_util.utc),
'name': 'Image test',
'project': EXAMPLE_PROJECT_ID,
'_created': datetime.datetime(2016, 5, 2, 14, 19, 37, 0, tzinfo=tz_util.utc),
'_etag': '6b8589b42c880e3626f43f3e82a5c5b946742687'
}
BLENDER_ID_TEST_USERID = 1533

View File

@ -8,4 +8,4 @@ TESTING = True
CDN_STORAGE_USER = 'u41508580125621'
FILESIZE_LIMIT_BYTES_NONSUBS = 20 * 2 ** 10
ROLES_FOR_UNLIMITED_UPLOADS = {u'subscriber', u'demo', u'admin'}
ROLES_FOR_UNLIMITED_UPLOADS = {'subscriber', 'demo', 'admin'}

View File

@ -1,7 +1,5 @@
"""Our custom Jinja filters and other template stuff."""
from __future__ import absolute_import
import logging
import flask

View File

@ -304,8 +304,8 @@ def feeds_blogs():
updated = post._updated if post._updated else post._created
url = url_for_node(node=post)
content = post.properties.content[:500]
content = u'<p>{0}... <a href="{1}">Read more</a></p>'.format(content, url)
feed.add(post.name, unicode(content),
content = '<p>{0}... <a href="{1}">Read more</a></p>'.format(content, url)
feed.add(post.name, str(content),
content_type='html',
author=author,
url=url,

View File

@ -25,7 +25,7 @@ def render_attachments(node, field_value):
node_attachments = node.properties.attachments or {}
if isinstance(node_attachments, list):
log.warning('Old-style attachments property found on node %s. Ignoring them, '
'will result in attachments not being found.', node[u'_id'])
'will result in attachments not being found.', node['_id'])
return field_value
if not node_attachments:
@ -37,7 +37,7 @@ def render_attachments(node, field_value):
try:
att = node_attachments[slug]
except KeyError:
return u'[attachment "%s" not found]' % slug
return '[attachment "%s" not found]' % slug
return render_attachment(att)
return shortcode_re.sub(replace, field_value)
@ -46,8 +46,8 @@ def render_attachments(node, field_value):
def render_attachment(attachment):
"""Renders an attachment as HTML"""
oid = ObjectId(attachment[u'oid'])
collection = attachment.collection or u'files'
oid = ObjectId(attachment['oid'])
collection = attachment.collection or 'files'
renderers = {
'files': render_attachment_file
@ -56,8 +56,8 @@ def render_attachment(attachment):
try:
renderer = renderers[collection]
except KeyError:
log.error(u'Unable to render attachment from collection %s', collection)
return u'Unable to render attachment'
log.error('Unable to render attachment from collection %s', collection)
return 'Unable to render attachment'
return renderer(attachment)
@ -66,7 +66,7 @@ def render_attachment_file(attachment):
"""Renders a file attachment."""
api = system_util.pillar_api()
sdk_file = pillarsdk.File.find(attachment[u'oid'], api=api)
sdk_file = pillarsdk.File.find(attachment['oid'], api=api)
file_renderers = {
'image': render_attachment_file_image
@ -119,7 +119,7 @@ def attachment_form_group_set_data(db_prop_value, schema_prop, field_list):
while len(field_list):
field_list.pop_entry()
for slug, att_data in sorted(db_prop_value.iteritems()):
for slug, att_data in sorted(db_prop_value.items()):
file_select_form_group = _attachment_build_single_field(schema_prop)
subform = file_select_form_group()

View File

@ -243,7 +243,7 @@ def comments_rate(comment_id, operation):
"""
if operation not in {u'revoke', u'upvote', u'downvote'}:
if operation not in {'revoke', 'upvote', 'downvote'}:
raise wz_exceptions.BadRequest('Invalid operation')
api = system_util.pillar_api()

View File

@ -33,7 +33,7 @@ def iter_node_properties(node_type):
node_schema = node_type['dyn_schema'].to_dict()
form_schema = node_type['form_schema'].to_dict()
for prop_name, prop_schema in node_schema.iteritems():
for prop_name, prop_schema in node_schema.items():
prop_fschema = form_schema.get(prop_name, {})
if not prop_fschema.get('visible', True):

View File

@ -142,7 +142,7 @@ def view(node_id):
return 'GET' in node.permissions.world
if current_user.is_authenticated:
allowed_roles = {u'subscriber', u'demo', u'admin'}
allowed_roles = {'subscriber', 'demo', 'admin'}
return bool(allowed_roles.intersection(current_user.roles or ()))
return False
@ -330,7 +330,7 @@ def edit(node_id):
log.debug('set_properties(..., prefix=%r, set_data=%r) called', prefix, set_data)
for prop, schema_prop in dyn_schema.iteritems():
for prop, schema_prop in dyn_schema.items():
prop_name = "{0}{1}".format(prefix, prop)
if prop_name not in form:
@ -376,7 +376,7 @@ def edit(node_id):
for file_data in db_prop_value:
file_form_class = build_file_select_form(subschema)
subform = file_form_class()
for key, value in file_data.iteritems():
for key, value in file_data.items():
setattr(subform, key, value)
field_list.append_entry(subform)
@ -481,7 +481,7 @@ def ensure_lists_exist_as_empty(node_doc, node_type):
node_properties = node_doc.setdefault('properties', {})
for prop, schema in node_type.dyn_schema.to_dict().iteritems():
for prop, schema in node_type.dyn_schema.to_dict().items():
if schema['type'] != 'list':
continue
@ -563,7 +563,7 @@ def redirect_to_context(node_id):
def url_for_node(node_id=None, node=None):
assert isinstance(node_id, (basestring, type(None)))
assert isinstance(node_id, (str, type(None)))
api = system_util.pillar_api()
@ -583,7 +583,7 @@ def url_for_node(node_id=None, node=None):
# Import of custom modules (using the same nodes decorator)
import custom.comments
import custom.groups
import custom.storage
import custom.posts
from . import custom.comments
from . import custom.groups
from . import custom.storage
from . import custom.posts

View File

@ -449,7 +449,7 @@ def edit(project_url):
url=project.url,
summary=project.summary,
description=project.description,
is_private=u'GET' not in project.permissions.world,
is_private='GET' not in project.permissions.world,
category=project.category,
status=project.status,
)
@ -471,7 +471,7 @@ def edit(project_url):
if form.is_private.data:
project.permissions.world = []
else:
project.permissions.world = [u'GET']
project.permissions.world = ['GET']
project.update(api=api)
# Reattach the pictures
@ -721,7 +721,7 @@ def project_update_nodes_list(node, project_id=None, list_name='latest'):
elif not project_id:
return None
project_id = node.project
if type(project_id) is not unicode:
if type(project_id) is not str:
project_id = node.project._id
api = system_util.pillar_api()
project = Project.find(project_id, api=api)

View File

@ -1,6 +1,6 @@
import logging
import string
import urlparse
import urllib.parse
from flask import Blueprint, redirect, current_app
from werkzeug.exceptions import NotFound
@ -54,7 +54,7 @@ def redirect_with_short_code(short_code):
# Redirect to 'theatre' view for the node.
url = url_for_node(node=node)
url = urlparse.urljoin(url, '?t')
url = urllib.parse.urljoin(url, '?t')
log.debug('Found short code %s, redirecting to %s', short_code, url)
return redirect(url, code=307)

View File

@ -2,7 +2,7 @@ import json
import logging
import httplib2 # used by the oauth2 package
import requests
import urlparse
import urllib.parse
from flask import (abort, Blueprint, current_app, flash, redirect,
render_template, request, session, url_for)
@ -274,15 +274,15 @@ def user_roles_update(user_id):
groups = set(user.groups or [])
if grant_subscriber:
roles.add(u'subscriber')
roles.add('subscriber')
groups.add(group_subscriber._id)
elif u'admin' not in roles:
elif 'admin' not in roles:
# Don't take away roles from admins.
roles.discard(u'subscriber')
roles.discard('subscriber')
groups.discard(group_subscriber._id)
if grant_demo:
roles.add(u'demo')
roles.add('demo')
groups.add(group_demo._id)
# Only send an API request when the user has actually changed
@ -316,7 +316,7 @@ def fetch_blenderid_user():
:rtype: dict
"""
bid_url = urlparse.urljoin(current_app.config['BLENDER_ID_ENDPOINT'], 'api/user')
bid_url = urllib.parse.urljoin(current_app.config['BLENDER_ID_ENDPOINT'], 'api/user')
log.debug('Fetching user info from %s', bid_url)
try:

View File

@ -1,6 +1,6 @@
import datetime
import hashlib
import urllib
import urllib.request, urllib.parse, urllib.error
import logging
import traceback
import sys
@ -52,7 +52,7 @@ def gravatar(email, size=64):
parameters = {'s': str(size), 'd': 'mm'}
return "https://www.gravatar.com/avatar/" + \
hashlib.md5(str(email)).hexdigest() + \
"?" + urllib.urlencode(parameters)
"?" + urllib.parse.urlencode(parameters)
def datetime_now():
@ -73,7 +73,7 @@ def pretty_date(time, detail=False, now=None):
# Normalize the 'time' parameter so it's always a datetime.
if type(time) is int:
time = datetime.datetime.fromtimestamp(time, tz=pillarsdk.utils.utc)
elif isinstance(time, basestring):
elif isinstance(time, str):
time = dateutil.parser.parse(time)
now = now or datetime.datetime.now(tz=time.tzinfo)
@ -184,10 +184,10 @@ def is_valid_id(some_id):
:rtype: bool
"""
if not isinstance(some_id, basestring):
if not isinstance(some_id, str):
return False
if isinstance(some_id, unicode):
if isinstance(some_id, str):
try:
some_id = some_id.encode('ascii')
except UnicodeEncodeError:

View File

@ -32,7 +32,7 @@ class CustomFileSelectWidget(HiddenInput):
if file_format and file_format == 'image':
file_format_regex = '^image\/(gif|jpe?g|png|tif?f|tga)$'
button = [u'<div class="form-upload-file">']
button = ['<div class="form-upload-file">']
if field.data:
api = system_util.pillar_api()
@ -42,64 +42,64 @@ class CustomFileSelectWidget(HiddenInput):
except ResourceNotFound:
pass
else:
button.append(u'<div class="form-upload-file-meta-container">')
button.append('<div class="form-upload-file-meta-container">')
filename = Markup.escape(file_item.filename)
if file_item.content_type.split('/')[0] == 'image':
# If a file of type image is available, display the preview
button.append(u'<img class="preview-thumbnail" src="{0}" />'.format(
button.append('<img class="preview-thumbnail" src="{0}" />'.format(
file_item.thumbnail('s', api=api)))
button.append(u'<ul class="form-upload-file-meta">')
button.append('<ul class="form-upload-file-meta">')
# File name
button.append(u'<li class="name">{0}</li>'.format(filename))
button.append('<li class="name">{0}</li>'.format(filename))
# File size
button.append(u'<li class="size">({0} MB)</li>'.format(
button.append('<li class="size">({0} MB)</li>'.format(
round((file_item.length / 1024) * 0.001, 2)))
# Image resolution (if image)
if file_item.content_type.split('/')[0] == 'image':
button.append(u'<li class="dimensions">{0}x{1}</li>'.format(
button.append('<li class="dimensions">{0}x{1}</li>'.format(
file_item.width, file_item.height))
button.append(u'</ul>')
button.append(u'<ul class="form-upload-file-actions">')
button.append('</ul>')
button.append('<ul class="form-upload-file-actions">')
# Download button for original file
button.append(u'<li class="original">'
u'<a href="{}" class="file_original"> '
u'<i class="pi-download"></i>Original</a></li>'
button.append('<li class="original">'
'<a href="{}" class="file_original"> '
'<i class="pi-download"></i>Original</a></li>'
.format(file_item.link))
# Delete button
button.append(u'<li class="delete">'
u'<a href="#" class="file_delete" '
u'data-field-name="{field_name}" '
u'data-file_id="{file_id}"> '
u'<i class="pi-trash"></i> Delete</a></li>'.format(
button.append('<li class="delete">'
'<a href="#" class="file_delete" '
'data-field-name="{field_name}" '
'data-file_id="{file_id}"> '
'<i class="pi-trash"></i> Delete</a></li>'.format(
field_name=field.name, file_id=field.data))
button.append(u'</ul>')
button.append(u'</div>')
button.append('</ul>')
button.append('</div>')
upload_url = u'%sstorage/stream/{project_id}' % current_app.config[
upload_url = '%sstorage/stream/{project_id}' % current_app.config[
'PILLAR_SERVER_ENDPOINT']
button.append(u'<input class="fileupload" type="file" name="file" '
u'data-url="{url}" '
u'data-field-name="{name}" '
u'data-field-slug="{slug}" '
u'data-token="{token}" '
u'data-file-format="{file_format}">'
u'<div class="form-upload-progress"> '
u'<div class="form-upload-progress-bar" role="progressbar" '
u'aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" '
u'style="width: 0%;"> '
u'</div> '
u'</div>'.format(url=upload_url,
button.append('<input class="fileupload" type="file" name="file" '
'data-url="{url}" '
'data-field-name="{name}" '
'data-field-slug="{slug}" '
'data-token="{token}" '
'data-file-format="{file_format}">'
'<div class="form-upload-progress"> '
'<div class="form-upload-progress-bar" role="progressbar" '
'aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" '
'style="width: 0%;"> '
'</div> '
'</div>'.format(url=upload_url,
name=field.name,
slug=field.name.replace('oid', 'slug'),
token=Markup.escape(current_user.id),
file_format=Markup.escape(file_format_regex)))
button.append(u'</div>')
button.append('</div>')
return HTMLString(html + u''.join(button))
return HTMLString(html + ''.join(button))
class FileSelectField(StringField):
@ -112,7 +112,7 @@ def build_file_select_form(schema):
class FileSelectForm(Form):
pass
for field_name, field_schema in schema.iteritems():
for field_name, field_schema in schema.items():
if field_schema['type'] == 'boolean':
field = BooleanField()
elif field_schema['type'] == 'string':
@ -142,19 +142,19 @@ class CustomFormFieldWidget(object):
def __call__(self, field, **kwargs):
html = []
kwargs.setdefault('id', field.id)
html.append(u'<div %s>' % html_params(**kwargs))
hidden = u''
html.append('<div %s>' % html_params(**kwargs))
hidden = ''
for subfield in field:
if subfield.type == 'HiddenField':
hidden += text_type(subfield)
else:
html.append(u'<div><span>%s</span>%s%s</div>' % (
html.append('<div><span>%s</span>%s%s</div>' % (
text_type(subfield.label), hidden, text_type(subfield)))
hidden = u''
html.append(u'</div>')
hidden = ''
html.append('</div>')
if hidden:
html.append(hidden)
return HTMLString(u''.join(html))
return HTMLString(''.join(html))
class CustomFormField(FormField):