checkpoint
|
@ -0,0 +1,48 @@
|
|||
*.key
|
||||
*.csr
|
||||
*.crt
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# =========================
|
||||
# Operating System Files
|
||||
# =========================
|
||||
|
||||
# OSX
|
||||
# =========================
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
|
@ -0,0 +1,114 @@
|
|||
start_time = time.time()
|
||||
|
||||
|
||||
# Raise for cases where the minimum > maximum
|
||||
for (maxkey, maxval) in maximums.items():
|
||||
if maxkey not in minimums:
|
||||
continue
|
||||
minval = minimums[maxkey]
|
||||
if minval > maxval:
|
||||
raise ValueError('Impossible min-max for %s' % maxkey)
|
||||
|
||||
conditions = []
|
||||
minmaxers = {'<=': maximums, '>=': minimums}
|
||||
|
||||
# Convert the min-max parameters into query strings
|
||||
print('Writing minmaxers')
|
||||
for (comparator, minmaxer) in minmaxers.items():
|
||||
for (field, value) in minmaxer.items():
|
||||
if field not in Photo.int_properties:
|
||||
raise ValueError('Unknown Photo property: %s' % field)
|
||||
|
||||
value = str(value)
|
||||
query = min_max_query_builder(field, comparator, value)
|
||||
conditions.append(query)
|
||||
|
||||
print(conditions)
|
||||
|
||||
print('Writing extension rule')
|
||||
if extension is not None:
|
||||
if isinstance(extension, str):
|
||||
extension = [extension]
|
||||
|
||||
# Normalize to prevent injections
|
||||
extension = [normalize_tagname(e) for e in extension]
|
||||
extension = ['extension == "%s"' % e for e in extension]
|
||||
extension = ' OR '.join(extension)
|
||||
extension = '(%s)' % extension
|
||||
conditions.append(extension)
|
||||
|
||||
def setify(l):
|
||||
if l is None:
|
||||
return set()
|
||||
else:
|
||||
return set(self.get_tag_by_name(t) for t in l)
|
||||
|
||||
tag_musts = setify(tag_musts)
|
||||
tag_mays = setify(tag_mays)
|
||||
tag_forbids = setify(tag_forbids)
|
||||
|
||||
base = '''
|
||||
{negator} EXISTS(
|
||||
SELECT 1 FROM photo_tag_rel
|
||||
WHERE photo_tag_rel.photoid == photos.id
|
||||
AND photo_tag_rel.tagid {operator} {value}
|
||||
)'''
|
||||
|
||||
print('Writing musts')
|
||||
for tag in tag_musts:
|
||||
# tagid == must
|
||||
query = base.format(
|
||||
negator='',
|
||||
operator='==',
|
||||
value='"%s"' % tag.id,
|
||||
)
|
||||
conditions.append(query)
|
||||
|
||||
print('Writing mays')
|
||||
if len(tag_mays) > 0:
|
||||
# not any(tagid not in mays)
|
||||
acceptable = tag_mays.union(tag_musts)
|
||||
acceptable = ['"%s"' % t.id for t in acceptable]
|
||||
acceptable = ', '.join(acceptable)
|
||||
query = base.format(
|
||||
negator='',
|
||||
operator='IN',
|
||||
value='(%s)' % acceptable,
|
||||
)
|
||||
conditions.append(query)
|
||||
|
||||
print('Writing forbids')
|
||||
if len(tag_forbids) > 0:
|
||||
# not any(tagid in forbids)
|
||||
forbids = ['"%s"' % t.id for t in tag_forbids]
|
||||
forbids = ', '.join(forbids)
|
||||
query = base.format(
|
||||
negator='NOT',
|
||||
operator='IN',
|
||||
value='(%s)' % forbids
|
||||
)
|
||||
conditions.append(query)
|
||||
|
||||
if len(conditions) == 0:
|
||||
raise ValueError('No search query provided')
|
||||
|
||||
conditions = [query for query in conditions if query is not None]
|
||||
conditions = ['(%s)' % c for c in conditions]
|
||||
conditions = ' AND '.join(conditions)
|
||||
conditions = 'WHERE %s' % conditions
|
||||
|
||||
query = 'SELECT * FROM photos %s' % conditions
|
||||
query = query.replace('\n', ' ')
|
||||
while ' ' in query:
|
||||
query = query.replace(' ', ' ')
|
||||
print(query)
|
||||
|
||||
temp_cur = self.sql.cursor()
|
||||
temp_cur.execute(query)
|
||||
|
||||
for fetch in fetch_generator(temp_cur):
|
||||
photo = Photo(self, fetch)
|
||||
yield photo
|
||||
|
||||
end_time = time.time()
|
||||
print(end_time - start_time)
|
|
@ -0,0 +1,465 @@
|
|||
import distutils.util
|
||||
import flask
|
||||
from flask import request
|
||||
import json
|
||||
import mimetypes
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import requests
|
||||
import warnings
|
||||
|
||||
site = flask.Flask(__name__)
|
||||
site.config.update(
|
||||
SEND_FILE_MAX_AGE_DEFAULT=180,
|
||||
TEMPLATES_AUTO_RELOAD=True,
|
||||
)
|
||||
|
||||
print(os.getcwd())
|
||||
import phototagger
|
||||
P = phototagger.PhotoDB()
|
||||
|
||||
FILE_READ_CHUNK = 2 ** 20
|
||||
|
||||
MOTD_STRINGS = [
|
||||
'Good morning, Paul. What will your first sequence of the day be?',
|
||||
#'Buckle up, it\'s time to:',
|
||||
]
|
||||
|
||||
ERROR_INVALID_ACTION = 'Invalid action'
|
||||
ERROR_NO_TAG_GIVEN = 'No tag name supplied'
|
||||
ERROR_TAG_TOO_SHORT = 'Not enough valid chars'
|
||||
ERROR_SYNONYM_ITSELF = 'Cant apply synonym to itself'
|
||||
ERROR_NO_SUCH_TAG = 'Doesn\'t exist'
|
||||
|
||||
def edit_params(original, modifications):
|
||||
new_params = original.to_dict()
|
||||
new_params.update(modifications)
|
||||
if not new_params:
|
||||
return ''
|
||||
keep_params = {}
|
||||
new_params = ['%s=%s' % (k, v) for (k, v) in new_params.items() if v]
|
||||
new_params = '&'.join(new_params)
|
||||
new_params = '?' + new_params
|
||||
return new_params
|
||||
|
||||
def P_album(albumid):
|
||||
try:
|
||||
return P.get_album(albumid)
|
||||
except phototagger.NoSuchAlbum:
|
||||
flask.abort(404, 'That tag doesnt exist')
|
||||
|
||||
def P_photo(photoid):
|
||||
try:
|
||||
return P.get_photo(photoid)
|
||||
except phototagger.NoSuchPhoto:
|
||||
flask.abort(404, 'That photo doesnt exist')
|
||||
|
||||
def P_tag(tagname):
|
||||
try:
|
||||
return P.get_tag(tagname)
|
||||
except phototagger.NoSuchTag as e:
|
||||
flask.abort(404, 'That tag doesnt exist: %s' % e)
|
||||
|
||||
def truthystring(s):
|
||||
if isinstance(s, (bool, int)) or s is None:
|
||||
return s
|
||||
s = s.lower()
|
||||
if s in {'1', 'true', 't', 'yes', 'y', 'on'}:
|
||||
return True
|
||||
if s in {'null', 'none'}:
|
||||
return None
|
||||
return False
|
||||
|
||||
def read_filebytes(filepath, range_min, range_max):
|
||||
range_span = range_max - range_min
|
||||
|
||||
#print('read span', range_min, range_max, range_span)
|
||||
f = open(filepath, 'rb')
|
||||
f.seek(range_min)
|
||||
sent_amount = 0
|
||||
with f:
|
||||
while sent_amount < range_span:
|
||||
chunk = f.read(FILE_READ_CHUNK)
|
||||
if len(chunk) == 0:
|
||||
break
|
||||
|
||||
yield chunk
|
||||
sent_amount += len(chunk)
|
||||
|
||||
def send_file(filepath):
|
||||
'''
|
||||
Range-enabled file sending.
|
||||
'''
|
||||
outgoing_headers = {}
|
||||
mimetype = mimetypes.guess_type(filepath)[0]
|
||||
if mimetype is not None:
|
||||
if 'text/' in mimetype:
|
||||
mimetype += '; charset=utf-8'
|
||||
outgoing_headers['Content-Type'] = mimetype
|
||||
|
||||
if 'range' not in request.headers:
|
||||
response = flask.make_response(flask.send_file(filepath))
|
||||
for (k, v) in outgoing_headers.items():
|
||||
response.headers[k] = v
|
||||
return response
|
||||
|
||||
try:
|
||||
file_size = os.path.getsize(filepath)
|
||||
except FileNotFoundError:
|
||||
flask.abort(404)
|
||||
|
||||
desired_range = request.headers['range'].lower()
|
||||
desired_range = desired_range.split('bytes=')[-1]
|
||||
|
||||
inthelper = lambda x: int(x) if x.isdigit() else None
|
||||
if '-' in desired_range:
|
||||
(desired_min, desired_max) = desired_range.split('-')
|
||||
range_min = inthelper(desired_min)
|
||||
range_max = inthelper(desired_max)
|
||||
else:
|
||||
range_min = inthelper(desired_range)
|
||||
|
||||
if range_min is None:
|
||||
range_min = 0
|
||||
if range_max is None:
|
||||
range_max = file_size
|
||||
|
||||
# because ranges are 0-indexed
|
||||
range_max = min(range_max, file_size - 1)
|
||||
range_min = max(range_min, 0)
|
||||
|
||||
range_header = 'bytes {min}-{max}/{outof}'.format(
|
||||
min=range_min,
|
||||
max=range_max,
|
||||
outof=file_size,
|
||||
)
|
||||
outgoing_headers['Content-Range'] = range_header
|
||||
outgoing_headers['Accept-Ranges'] = 'bytes'
|
||||
outgoing_headers['Content-Length'] = (range_max - range_min) + 1
|
||||
|
||||
outgoing_data = read_filebytes(filepath, range_min=range_min, range_max=range_max)
|
||||
response = flask.Response(
|
||||
outgoing_data,
|
||||
status=206,
|
||||
headers=outgoing_headers,
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
@site.route('/')
|
||||
def root():
|
||||
motd = random.choice(MOTD_STRINGS)
|
||||
return flask.render_template('root.html', motd=motd)
|
||||
|
||||
@site.route('/album/<albumid>')
|
||||
def get_album(albumid):
|
||||
album = P_album(albumid)
|
||||
response = flask.render_template(
|
||||
'album.html',
|
||||
album=album,
|
||||
child_albums=album.children(),
|
||||
photos=album.photos()
|
||||
)
|
||||
return response
|
||||
|
||||
@site.route('/file/<photoid>')
|
||||
def get_file(photoid):
|
||||
requested_photoid = photoid
|
||||
photoid = photoid.split('.')[0]
|
||||
photo = P.get_photo(photoid)
|
||||
|
||||
do_download = request.args.get('download', False)
|
||||
do_download = truthystring(do_download)
|
||||
|
||||
use_original_filename = request.args.get('original_filename', False)
|
||||
use_original_filename = truthystring(use_original_filename)
|
||||
|
||||
if do_download:
|
||||
if use_original_filename:
|
||||
download_as = photo.basename
|
||||
else:
|
||||
download_as = photo.id + '.' + photo.extension
|
||||
|
||||
# Sorry, but otherwise the attachment filename gets terminated
|
||||
#download_as = download_as.replace(';', '-')
|
||||
download_as = download_as.replace('"', '\\"')
|
||||
response = flask.make_response(send_file(photo.real_filepath))
|
||||
response.headers['Content-Disposition'] = 'attachment; filename="%s"' % download_as
|
||||
return response
|
||||
else:
|
||||
return send_file(photo.real_filepath)
|
||||
|
||||
@site.route('/albums')
|
||||
def get_albums():
|
||||
albums = P.get_albums()
|
||||
albums = [a for a in albums if a.parent() is None]
|
||||
return flask.render_template('albums.html', albums=albums)
|
||||
|
||||
@site.route('/photo/<photoid>', methods=['GET'])
|
||||
def get_photo(photoid):
|
||||
photo = P_photo(photoid)
|
||||
tags = photo.tags()
|
||||
tags.sort(key=lambda x: x.qualified_name())
|
||||
return flask.render_template('photo.html', photo=photo, tags=tags)
|
||||
|
||||
@site.route('/tags')
|
||||
@site.route('/tags/<specific_tag>')
|
||||
def get_tags(specific_tag=None):
|
||||
try:
|
||||
tags = P.export_tags(phototagger.tag_export_easybake, specific_tag=specific_tag)
|
||||
except phototagger.NoSuchTag:
|
||||
flask.abort(404, 'That tag doesnt exist')
|
||||
|
||||
tags = tags.split('\n')
|
||||
tags = [t for t in tags if t != '']
|
||||
tags = [(t, t.split('.')[-1].split('+')[0]) for t in tags]
|
||||
return flask.render_template('tags.html', tags=tags)
|
||||
|
||||
@site.route('/thumbnail/<photoid>')
|
||||
def get_thumbnail(photoid):
|
||||
photoid = photoid.split('.')[0]
|
||||
photo = P_photo(photoid)
|
||||
if photo.thumbnail:
|
||||
path = photo.thumbnail
|
||||
else:
|
||||
flask.abort(404, 'That file doesnt have a thumbnail')
|
||||
return send_file(path)
|
||||
|
||||
@site.route('/photo/<photoid>', methods=['POST'])
|
||||
def edit_photo(photoid):
|
||||
print(request.form)
|
||||
response = {}
|
||||
photo = P_photo(photoid)
|
||||
|
||||
if 'add_tag' in request.form:
|
||||
action = 'add_tag'
|
||||
method = photo.add_tag
|
||||
elif 'remove_tag' in request.form:
|
||||
action = 'remove_tag'
|
||||
method = photo.remove_tag
|
||||
else:
|
||||
flask.abort(400, 'Invalid action')
|
||||
|
||||
tag = request.form[action].strip()
|
||||
if tag == '':
|
||||
flask.abort(400, 'No tag supplied')
|
||||
|
||||
try:
|
||||
tag = P.get_tag(tag)
|
||||
except phototagger.NoSuchTag:
|
||||
return flask.Response('{"error": "That tag doesnt exist", "tagname":"%s"}'%tag, status=404)
|
||||
|
||||
method(tag)
|
||||
response['action'] = action
|
||||
response['tagid'] = tag.id
|
||||
response['tagname'] = tag.name
|
||||
return json.dumps(response)
|
||||
|
||||
@site.route('/tags', methods=['POST'])
|
||||
def edit_tags():
|
||||
print(request.form)
|
||||
status = 200
|
||||
if 'create_tag' in request.form:
|
||||
action = 'create_tag'
|
||||
method = create_tag
|
||||
elif 'delete_tag_synonym' in request.form:
|
||||
action = 'delete_tag_synonym'
|
||||
method = delete_synonym
|
||||
elif 'delete_tag' in request.form:
|
||||
action = 'delete_tag'
|
||||
method = delete_tag
|
||||
else:
|
||||
response = {'error': ERROR_INVALID_ACTION}
|
||||
|
||||
if status == 200:
|
||||
status = 400
|
||||
tag = request.form[action].strip()
|
||||
if tag == '':
|
||||
response = {'error': ERROR_NO_TAG_GIVEN}
|
||||
try:
|
||||
response = method(tag)
|
||||
except phototagger.TagTooShort:
|
||||
response = {'error': ERROR_TAG_TOO_SHORT, 'tagname': tag}
|
||||
except phototagger.CantSynonymSelf:
|
||||
response = {'error': ERROR_SYNONYM_ITSELF, 'tagname': tag}
|
||||
except phototagger.NoSuchTag as e:
|
||||
response = {'error': ERROR_NO_SUCH_TAG, 'tagname': tag}
|
||||
except ValueError as e:
|
||||
response = {'error': e.args[0], 'tagname': tag}
|
||||
else:
|
||||
status = 200
|
||||
|
||||
response = json.dumps(response)
|
||||
response = flask.Response(response, status=status)
|
||||
return response
|
||||
|
||||
def create_tag(easybake_string):
|
||||
notes = P.easybake(easybake_string)
|
||||
notes = [{'action': action, 'tagname': tagname} for (action, tagname) in notes]
|
||||
return notes
|
||||
|
||||
def delete_tag(tag):
|
||||
tag = tag.split('.')[-1].split('+')[0]
|
||||
tag = P.get_tag(tag)
|
||||
|
||||
tag.delete()
|
||||
return {'action': 'delete_tag', 'tagname': tag.name, 'tagid': tag.id}
|
||||
|
||||
def delete_synonym(synonym):
|
||||
synonym = synonym.split('+')[-1].split('.')[-1]
|
||||
synonym = phototagger.normalize_tagname(synonym)
|
||||
try:
|
||||
master_tag = P.get_tag(synonym)
|
||||
except phototagger.NoSuchTag:
|
||||
flask.abort(404, 'That synonym doesnt exist')
|
||||
|
||||
if synonym not in master_tag.synonyms():
|
||||
flask.abort(400, 'That name is not a synonym')
|
||||
|
||||
master_tag.remove_synonym(synonym)
|
||||
return {'action':'delete_synonym', 'synonym': synonym}
|
||||
|
||||
|
||||
@site.route('/search')
|
||||
def search():
|
||||
print(request.args)
|
||||
|
||||
def comma_split_helper(s):
|
||||
s = s.replace(' ', ',')
|
||||
s = [x.strip() for x in s.split(',')]
|
||||
s = [x for x in s if x]
|
||||
return s
|
||||
# EXTENSION
|
||||
extension_string = request.args.get('extension', '')
|
||||
extension_not_string = request.args.get('extension_not', '')
|
||||
mimetype_string = request.args.get('mimetype', '')
|
||||
|
||||
extension_list = comma_split_helper(extension_string)
|
||||
extension_not_list = comma_split_helper(extension_not_string)
|
||||
mimetype_list = comma_split_helper(mimetype_string)
|
||||
|
||||
# LIMIT
|
||||
limit = request.args.get('limit', '')
|
||||
if limit.isdigit():
|
||||
limit = int(limit)
|
||||
limit = min(100, limit)
|
||||
else:
|
||||
limit = 50
|
||||
|
||||
# OFFSET
|
||||
offset = request.args.get('offset', None)
|
||||
if offset:
|
||||
offset = int(offset)
|
||||
else:
|
||||
offset = None
|
||||
|
||||
# MUSTS, MAYS, FORBIDS
|
||||
qualname_map = P.export_tags(exporter=phototagger.tag_export_qualname_map)
|
||||
tag_musts = request.args.get('tag_musts', '').split(',')
|
||||
tag_mays = request.args.get('tag_mays', '').split(',')
|
||||
tag_forbids = request.args.get('tag_forbids', '').split(',')
|
||||
tag_expression = request.args.get('tag_expression', None)
|
||||
|
||||
tag_musts = [qualname_map.get(tag, tag) for tag in tag_musts if tag != '']
|
||||
tag_mays = [qualname_map.get(tag, tag) for tag in tag_mays if tag != '']
|
||||
tag_forbids = [qualname_map.get(tag, tag) for tag in tag_forbids if tag != '']
|
||||
|
||||
# ORDERBY
|
||||
orderby = request.args.get('orderby', None)
|
||||
if orderby:
|
||||
orderby = orderby.replace('-', ' ')
|
||||
orderby = orderby.replace('_', ' ')
|
||||
orderby = orderby.split(',')
|
||||
else:
|
||||
orderby = None
|
||||
|
||||
# HAS_TAGS
|
||||
has_tags = request.args.get('has_tags', '')
|
||||
if has_tags == '':
|
||||
has_tags = None
|
||||
else:
|
||||
has_tags = truthystring(has_tags)
|
||||
|
||||
# MINMAXERS
|
||||
area = request.args.get('area', None)
|
||||
width = request.args.get('width', None)
|
||||
height = request.args.get('height', None)
|
||||
ratio = request.args.get('ratio', None)
|
||||
bytes = request.args.get('bytes', None)
|
||||
length = request.args.get('length', None)
|
||||
created = request.args.get('created', None)
|
||||
|
||||
# These are in a dictionary so I can pass them to the page template.
|
||||
search_kwargs = {
|
||||
'area': area,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'ratio': ratio,
|
||||
'bytes': bytes,
|
||||
'length': length,
|
||||
|
||||
'created': created,
|
||||
'extension': extension_list,
|
||||
'extension_not': extension_not_list,
|
||||
'has_tags': has_tags,
|
||||
'mimetype': mimetype_list,
|
||||
'tag_musts': tag_musts,
|
||||
'tag_mays': tag_mays,
|
||||
'tag_forbids': tag_forbids,
|
||||
'tag_expression': tag_expression,
|
||||
|
||||
'limit': limit,
|
||||
'offset': offset,
|
||||
'orderby': orderby,
|
||||
|
||||
'warn_bad_tags': True,
|
||||
}
|
||||
print(search_kwargs)
|
||||
with warnings.catch_warnings(record=True) as catcher:
|
||||
photos = list(P.search(**search_kwargs))
|
||||
warns = [str(warning.message) for warning in catcher]
|
||||
print(warns)
|
||||
total_tags = set()
|
||||
for photo in photos:
|
||||
total_tags.update(photo.tags())
|
||||
for tag in total_tags:
|
||||
tag._cached_qualname = qualname_map[tag.name]
|
||||
total_tags = sorted(total_tags, key=lambda x: x._cached_qualname)
|
||||
|
||||
# PREV-NEXT PAGE URLS
|
||||
offset = offset or 0
|
||||
if len(photos) == limit:
|
||||
next_params = edit_params(request.args, {'offset': offset + limit})
|
||||
next_page_url = '/search' + next_params
|
||||
else:
|
||||
next_page_url = None
|
||||
if offset > 0:
|
||||
prev_params = edit_params(request.args, {'offset': max(0, offset - limit)})
|
||||
prev_page_url = '/search' + prev_params
|
||||
else:
|
||||
prev_page_url = None
|
||||
|
||||
search_kwargs['extension'] = extension_string
|
||||
search_kwargs['extension_not'] = extension_not_string
|
||||
search_kwargs['mimetype'] = mimetype_string
|
||||
response = flask.render_template(
|
||||
'search.html',
|
||||
photos=photos,
|
||||
search_kwargs=search_kwargs,
|
||||
total_tags=total_tags,
|
||||
prev_page_url=prev_page_url,
|
||||
next_page_url=next_page_url,
|
||||
qualname_map=json.dumps(qualname_map),
|
||||
warns=warns,
|
||||
)
|
||||
return response
|
||||
|
||||
@site.route('/static/<filename>')
|
||||
def get_resource(filename):
|
||||
print(filename)
|
||||
return flask.send_file('.\\static\\%s' % filename)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
site.run(threaded=True)
|
|
@ -0,0 +1,25 @@
|
|||
# Use with
|
||||
# py -i etiquette_easy.py
|
||||
|
||||
import phototagger
|
||||
P = phototagger.PhotoDB()
|
||||
import traceback
|
||||
|
||||
def easytagger():
|
||||
while True:
|
||||
i = input('> ')
|
||||
if i.startswith('?'):
|
||||
i = i.split('?')[1] or None
|
||||
try:
|
||||
P.export_tags(specific_tag=i)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
continue
|
||||
P.easybake(i)
|
||||
|
||||
def photag(photoid):
|
||||
photo = P.get_photo_by_id(photoid)
|
||||
print(photo.tags())
|
||||
while True:
|
||||
photo.add_tag(input('> '))
|
||||
get=P.get_tag
|
|
@ -0,0 +1,25 @@
|
|||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
import etiquette
|
||||
import gevent.pywsgi
|
||||
import gevent.wsgi
|
||||
import sys
|
||||
|
||||
if len(sys.argv) == 2:
|
||||
port = int(sys.argv[1])
|
||||
else:
|
||||
port = 5000
|
||||
|
||||
if port == 443:
|
||||
http = gevent.pywsgi.WSGIServer(
|
||||
('', port),
|
||||
etiquette.site,
|
||||
keyfile='etiquette.key',
|
||||
certfile='etiquette.crt',
|
||||
)
|
||||
else:
|
||||
http = gevent.wsgi.WSGIServer(('', port), etiquette.site)
|
||||
|
||||
print('Starting server')
|
||||
http.serve_forever()
|
|
@ -0,0 +1,16 @@
|
|||
for p in P.get_photos():
|
||||
g=P.cur.execute('UPDATE photos SET id==? WHERE id==?', [p.id[-12:], p.id])
|
||||
g=P.cur.execute('UPDATE photo_tag_rel SET photoid==? WHERE photoid==?', [p.id[-12:], p.id])
|
||||
g=P.cur.execute('UPDATE album_photo_rel SET photoid==? WHERE photoid==?', [p.id[-12:], p.id])
|
||||
|
||||
for t in P.get_tags():
|
||||
g=P.cur.execute('UPDATE tags SET id==? WHERE id==?', [t.id[-12:], t.id])
|
||||
g=P.cur.execute('UPDATE photo_tag_rel SET tagid==? WHERE tagid==?', [t.id[-12:], t.id])
|
||||
g=P.cur.execute('UPDATE tag_group_rel SET parentid==? WHERE parentid==?', [t.id[-12:], t.id])
|
||||
g=P.cur.execute('UPDATE tag_group_rel SET memberid==? WHERE memberid==?', [t.id[-12:], t.id])
|
||||
|
||||
for a in P.get_albums():
|
||||
g=P.cur.execute('UPDATE albums SET id==? WHERE id==?', [a.id[-12:], a.id])
|
||||
g=P.cur.execute('UPDATE tag_group_rel SET parentid==? WHERE parentid==?', [a.id[-12:], a.id])
|
||||
g=P.cur.execute('UPDATE tag_group_rel SET memberid==? WHERE memberid==?', [a.id[-12:], a.id])
|
||||
g=P.cur.execute('UPDATE album_photo_rel SET albumid==? WHERE albumid==?', [a.id[-12:], a.id])
|
After Width: | Height: | Size: 467 KiB |
After Width: | Height: | Size: 900 KiB |
After Width: | Height: | Size: 321 KiB |
After Width: | Height: | Size: 63 KiB |
|
@ -0,0 +1,141 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="400"
|
||||
height="400"
|
||||
viewBox="0 0 400.00002 400"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="audio.svg"
|
||||
inkscape:export-filename="C:\Git\else\Etiquette\static\basic_thumbnails\audio.png"
|
||||
inkscape:export-xdpi="144"
|
||||
inkscape:export-ydpi="144">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4723">
|
||||
<stop
|
||||
style="stop-color:#157a05;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4725" />
|
||||
<stop
|
||||
style="stop-color:#8cec27;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4727" />
|
||||
</linearGradient>
|
||||
<clipPath
|
||||
id="clipPath5600"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5602"
|
||||
d="m 416.04743,209.57467 a 210.71427,210.71427 0 0 0 -201.63871,156.16785 210.71427,210.71427 0 0 0 148.9961,258.07232 210.71427,210.71427 0 0 0 258.07031,-148.99795 210.71427,210.71427 0 0 0 -148.9961,-258.07035 210.71427,210.71427 0 0 0 -56.4316,-7.17187 z m 0.088,10 a 200.71428,200.71428 0 0 1 53.75188,6.83 A 200.71428,200.71428 0 0 1 611.81495,472.22889 200.71428,200.71428 0 0 1 365.99079,614.15457 200.71428,200.71428 0 0 1 224.0669,368.3325 200.71428,200.71428 0 0 1 416.13517,219.57467 Z m 0.67771,10.66015 c -28.9322,-0.0158 -57.57612,6.77648 -84.07229,20.47847 -5.58051,2.89034 -7.71121,5.24233 -5.71092,8.93562 l 84.62499,146.57421 c 3.9199,6.67208 6.92071,6.5608 8.37693,-0.1625 L 463.9225,242.25987 c 1.2404,-4.75141 0.77432,-5.32621 -7.65239,-7.81065 -13.09518,-2.80222 -26.30598,-4.21013 -39.45697,-4.21472 z m 55.19328,8.75394 c -2.49601,-0.0656 -4.01546,0.93883 -4.7636,3.45495 l -43.80467,163.4825 c -1.94613,7.48962 0.25549,9.53469 6.03899,5.81055 l 146.85941,-84.78907 c 4.23688,-2.48265 4.31319,-3.22025 0.11141,-10.93556 -23.2904,-35.97209 -57.50302,-62.6885 -98.8477,-75.84771 -2.24622,-0.71332 -4.09611,-1.13676 -5.59382,-1.17566 z m -150.93155,19.66206 c -1.45873,-0.0237 -3.57203,1.00841 -7.42973,3.10929 -35.97206,23.29037 -62.69049,57.50296 -75.8496,98.84772 -1.90228,5.98981 -1.74657,9.15834 2.2793,10.35536 l 163.48439,43.80474 c 7.48959,1.94628 9.5327,-0.25337 5.8086,-6.03706 l -84.78918,-146.8593 c -1.2413,-2.11844 -2.04519,-3.19729 -3.50392,-3.22075 z m 260.03112,70.01171 c -0.7849,-0.0158 -1.62359,0.23625 -2.54681,0.73629 l -146.57616,84.62511 c -6.67213,3.91977 -6.56093,6.92076 0.16224,8.37677 l 163.8008,43.89074 c 4.75141,1.24035 5.32608,0.77437 7.8105,-7.65244 8.96741,-41.90478 3.66661,-84.98932 -16.2636,-123.52924 -2.1678,-4.18535 -4.0319,-6.43032 -6.38678,-6.44723 z m -344.04481,45.09563 c -2.28245,0 -3.07026,1.84403 -4.93359,8.16404 -8.96736,41.90477 -3.66848,84.9895 16.2617,123.52942 2.89032,5.58046 5.2424,7.71121 8.93556,5.71094 l 146.57419,-84.62495 c 6.67208,-3.91978 6.5628,-6.92092 -0.16013,-8.37709 L 239.93603,374.26994 c -1.18786,-0.31005 -2.1142,-0.51275 -2.87501,-0.51178 z m 190.67183,51.3438 c -3.55996,0.069 -4.01188,2.39073 -1.21878,6.72855 l 84.79113,146.86128 c 2.48259,4.23704 3.2182,4.31319 10.93359,0.11167 35.97198,-23.29037 62.6885,-57.50509 75.8476,-98.8497 1.90228,-5.98979 1.7466,-9.1585 -2.27929,-10.35552 L 432.3247,425.79345 c -1.87239,-0.4865 -3.4051,-0.7143 -4.5918,-0.69133 z m -16.97067,1.91211 c -1.09041,0.0477 -2.54811,0.64684 -4.35552,1.81055 l -146.8593,84.79104 c -4.23692,2.48265 -4.31319,3.21828 -0.11142,10.93359 23.2903,35.97209 57.50296,62.6885 98.84765,75.84755 5.98981,1.90226 9.15839,1.74668 10.35542,-2.27929 l 43.80668,-163.48234 c 1.33799,-5.14927 0.7152,-7.72466 -1.68359,-7.6211 z m 8.35348,2.38285 c -1.42229,0.0345 -2.53748,1.74194 -3.26558,5.1035 L 371.9615,598.30117 c -1.2405,4.75143 -0.7744,5.32621 7.65242,7.8105 41.90477,8.96746 84.98929,3.66653 123.52929,-16.26358 5.58051,-2.89034 7.7112,-5.24251 5.71091,-8.93562 l -84.625,-146.57421 c -1.95992,-3.33596 -3.69103,-4.97547 -5.11333,-4.94134 z"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4723"
|
||||
id="linearGradient4739"
|
||||
x1="196.13852"
|
||||
y1="903.96155"
|
||||
x2="177.04013"
|
||||
y2="792.60211"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.74021631,-0.67236881,0.67236881,0.74021631,-530.7894,361.49036)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.67124999"
|
||||
inkscape:cx="199.31367"
|
||||
inkscape:cy="266.33033"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="706"
|
||||
inkscape:window-x="1432"
|
||||
inkscape:window-y="204"
|
||||
inkscape:window-maximized="1"
|
||||
units="px"
|
||||
showguides="false" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-652.36221)">
|
||||
<path
|
||||
cx="356.42856"
|
||||
cy="515.93359"
|
||||
r="190.61275"
|
||||
id="path4146"
|
||||
style="opacity:1;fill:#f2dc39;fill-opacity:1;stroke:none;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d=""
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
cx="356.42856"
|
||||
cy="515.93359"
|
||||
r="190.61275"
|
||||
id="path4148"
|
||||
style="opacity:1;fill:#f2dc39;fill-opacity:1;stroke:none;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d=""
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
id="g4759"
|
||||
transform="matrix(2.0529613,0,0,2.0529613,-208.02761,-862.66247)">
|
||||
<path
|
||||
id="path4176"
|
||||
d="m 160.49771,775.78875 c -17.18761,15.61221 -30.85175,51.16826 -26.54481,80.55638 -5.68961,15.27438 -2.23888,26.77908 5.86857,35.70464 8.41129,9.26005 20.42276,13.46722 36.74655,8.76774 28.38226,5.82869 63.31581,-4.19178 79.95892,-19.3094 z"
|
||||
style="opacity:1;fill:url(#linearGradient4739);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccsccc" />
|
||||
<path
|
||||
style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00000012;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 187.71478,901.36194 c -15.6087,-1.19621 -30.19953,-6.46109 -40.11858,-17.38106 -3.59314,-3.95572 -6.38887,-8.45054 -8.4789,-13.31879"
|
||||
id="path4179"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="csc" />
|
||||
<ellipse
|
||||
transform="matrix(0.74021631,-0.67236881,0.67236881,0.74021631,0,0)"
|
||||
ry="71.356659"
|
||||
rx="39.683559"
|
||||
cy="753.57629"
|
||||
cx="-400.81314"
|
||||
id="path4189"
|
||||
style="opacity:1;fill:#bdff7f;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.10868788;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="circle5376"
|
||||
d="m 167.97402,779.73201 c 12.95394,14.14301 25.67999,28.49473 38.76045,42.52164 1.09188,1.09288 1.57468,2.03532 1.12908,0.24144 -3.81326,-15.37536 -7.34553,-30.80883 -11.73933,-46.02909 -9.33962,-3.59468 -21.01947,-4.53564 -29.35443,1.94027 z m -3.40652,1.08631 c -6.35037,7.84741 -6.53271,18.97849 -3.33194,28.11951 14.61665,5.15311 29.16348,10.48467 43.81316,15.53604 2.17968,0.89623 1.61044,0.35664 0.43632,-0.95963 -13.16593,-14.74917 -26.57361,-29.28008 -39.82726,-43.95011 l -1.09034,1.25425 z m 34.52343,-0.80156 10.96907,43.65806 c 0.3333,2.09058 0.60304,1.61585 1.77113,0.51394 7.58595,-7.11871 15.71168,-13.70923 22.85068,-21.25393 -10.11646,-11.06159 -22.40233,-20.30395 -36.34759,-25.93267 z m -36.91782,33.66852 c 4.32187,13.48593 12.1718,25.57007 21.57681,36.03286 l 22.88728,-20.78947 c 1.94371,-1.55853 0.90464,-1.6299 -0.63476,-2.19321 L 161.23898,810.7949 Z m 52.1086,14.12822 44.66065,15.90394 c -4.32392,-14.54689 -12.41384,-27.7685 -22.56898,-38.9523 l -23.01225,20.90297 c -1.67223,1.35326 -1.17245,1.5415 0.92058,2.14539 z m -28.6948,23.78756 c 9.97733,11.13211 22.30826,20.25112 36.19297,25.86994 l -11.81394,-47.01872 c -0.24809,-1.18978 -0.36986,-1.35104 -1.49167,-0.33205 l -23.22275,21.09417 0.32766,0.37768 0.008,0.009 z m 27.58834,-15.54751 c 3.45815,13.76858 6.91919,27.53657 10.37846,41.30493 9.14559,4.6969 21.26842,5.13014 29.7965,-1.27057 -13.33458,-14.60073 -26.5137,-29.34346 -39.93482,-43.86507 -0.84001,-1.17611 -1.91288,-2.39494 -1.22768,-0.34375 z m 3.75391,-2.44675 37.61326,41.40874 c 7.1511,-7.87583 7.83178,-19.8138 4.31773,-29.47142 -14.6668,-5.17311 -29.26398,-10.5241 -43.96637,-15.58822 -2.34003,-1.03127 -1.12256,0.0763 -0.0843,1.25149 0.7066,0.79977 1.41314,1.59962 2.11974,2.39938 z"
|
||||
style="opacity:1;fill:#6bd625;fill-opacity:1;stroke:none;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 19 KiB |
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="400"
|
||||
height="400"
|
||||
viewBox="0 0 400.00002 400"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="other.svg"
|
||||
inkscape:export-filename="C:\Git\else\Etiquette\static\basic_thumbnails\other.png"
|
||||
inkscape:export-xdpi="185.87027"
|
||||
inkscape:export-ydpi="185.87027">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4361">
|
||||
<stop
|
||||
style="stop-color:#4d4d4d;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4363" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4365" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4361"
|
||||
id="linearGradient4367"
|
||||
x1="315.47162"
|
||||
y1="1037.2178"
|
||||
x2="204.01379"
|
||||
y2="699.85175"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8225722,0,0,1.3309496,35.485561,-282.08894)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.94929084"
|
||||
inkscape:cx="51.384844"
|
||||
inkscape:cy="169.83481"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="706"
|
||||
inkscape:window-x="1432"
|
||||
inkscape:window-y="204"
|
||||
inkscape:window-maximized="1"
|
||||
units="px" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-652.36221)">
|
||||
<rect
|
||||
style="opacity:1;fill:url(#linearGradient4367);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.18531179;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="rect4359"
|
||||
width="251.81471"
|
||||
height="251.8147"
|
||||
x="74.092659"
|
||||
y="726.45483" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 21 KiB |
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="400"
|
||||
height="400"
|
||||
viewBox="0 0 400.00002 400"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="video.svg"
|
||||
inkscape:export-filename="C:\Git\else\Etiquette\static\basic_thumbnails\video.png"
|
||||
inkscape:export-xdpi="143.99998"
|
||||
inkscape:export-ydpi="143.99998">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4160">
|
||||
<stop
|
||||
style="stop-color:#414141;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4162" />
|
||||
<stop
|
||||
id="stop4172"
|
||||
offset="0.44465271"
|
||||
style="stop-color:#a5a5a5;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4168"
|
||||
offset="0.51481694"
|
||||
style="stop-color:#cccccc;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4164" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4160"
|
||||
id="linearGradient4166"
|
||||
x1="218.28458"
|
||||
y1="950.68616"
|
||||
x2="213.38019"
|
||||
y2="742.12012"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.335625"
|
||||
inkscape:cx="833.60008"
|
||||
inkscape:cy="34.45227"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="706"
|
||||
inkscape:window-x="1432"
|
||||
inkscape:window-y="204"
|
||||
inkscape:window-maximized="1"
|
||||
units="px" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-652.36221)">
|
||||
<path
|
||||
style="opacity:1;fill:url(#linearGradient4166);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
inkscape:transform-center-x="-38.11878"
|
||||
inkscape:transform-center-y="-2.5060727e-005"
|
||||
d="M 317.15321,852.36221 88.440457,984.40954 c 0,0 40.204773,-53.51798 40.204773,-132.04733 0,-78.52935 -40.204773,-132.0474 -40.204773,-132.0474 z"
|
||||
id="path4157"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cczcc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,171 @@
|
|||
body
|
||||
{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color:#00d8f4;
|
||||
margin: 8px;
|
||||
}
|
||||
li
|
||||
{
|
||||
position:relative;
|
||||
}
|
||||
#header
|
||||
{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.header_element
|
||||
{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.header_element:hover
|
||||
{
|
||||
background-color: #ffffd4;
|
||||
}
|
||||
#content_body
|
||||
{
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.add_tag_button, #search_go_button
|
||||
{
|
||||
border-top: 2px solid #c2ffc3;
|
||||
border-left: 2px solid #c2ffc3;
|
||||
border-right: 2px solid #259427;
|
||||
border-bottom: 2px solid #259427;
|
||||
background-color: #6df16f;
|
||||
}
|
||||
.add_tag_button:active, #search_go_button:active
|
||||
{
|
||||
border-top: 2px solid #259427;
|
||||
border-left: 2px solid #259427;
|
||||
border-right: 2px solid #c2ffc3;
|
||||
border-bottom: 2px solid #c2ffc3;
|
||||
}
|
||||
.remove_tag_button,
|
||||
.remove_tag_button_perm
|
||||
{
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
width: 18px;
|
||||
height: 14px;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
background-color: #ff4949;
|
||||
border-top: 2px solid #ffacac;
|
||||
border-left: 2px solid #ffacac;
|
||||
border-right: 2px solid #bd1b1b;
|
||||
border-bottom: 2px solid #bd1b1b;
|
||||
}
|
||||
.remove_tag_button
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
.tag_object:hover + .remove_tag_button,
|
||||
.remove_tag_button:hover,
|
||||
.remove_tag_button_perm:hover
|
||||
{
|
||||
display:inline;
|
||||
}
|
||||
.remove_tag_button:active,
|
||||
.remove_tag_button_perm:active
|
||||
{
|
||||
border-top: 2px solid #bd1b1b;
|
||||
border-left: 2px solid #bd1b1b;
|
||||
border-right: 2px solid #ffacac;
|
||||
border-bottom: 2px solid #ffacac;
|
||||
}
|
||||
.photo_galleryview
|
||||
{
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.25);
|
||||
display: inline-block;
|
||||
min-width: 150px;
|
||||
max-width: 300px;
|
||||
height: 200px;
|
||||
background-color: #ffffd4;
|
||||
padding: 8px;
|
||||
margin: 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.photo_galleryview_thumb
|
||||
{
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
.photo_galleryview_thumb a
|
||||
{
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
.photo_galleryview_thumb img
|
||||
{
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.photo_galleryview_info
|
||||
{
|
||||
position: absolute;
|
||||
top: 160px;
|
||||
bottom: 0px;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.photo_galleryview_info a
|
||||
{
|
||||
position: absolute;
|
||||
max-height: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.photo_galleryview_info a:hover
|
||||
{
|
||||
max-height: 100%;
|
||||
}
|
||||
.photo_galleryview_info span
|
||||
{
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
.tag_object
|
||||
{
|
||||
font-size: 0.9em;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
text-decoration: none;
|
||||
font-family: monospace;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.callback_message_positive, .callback_message_negative
|
||||
{
|
||||
width: 80%;
|
||||
margin: 4px;
|
||||
}
|
||||
.callback_message_positive
|
||||
{
|
||||
background-color: #afa;
|
||||
}
|
||||
.callback_message_negative
|
||||
{
|
||||
background-color: #faa;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
function create_message_bubble(message_positivity, message_text, lifespan)
|
||||
{
|
||||
if (lifespan === undefined)
|
||||
{
|
||||
lifespan = 8000;
|
||||
}
|
||||
var message = document.createElement("div");
|
||||
message.className = message_positivity;
|
||||
var span = document.createElement("span");
|
||||
span.innerHTML = message_text;
|
||||
message.appendChild(span);
|
||||
message_area.appendChild(message);
|
||||
setTimeout(function(){message_area.removeChild(message);}, lifespan);
|
||||
}
|
||||
|
||||
function add_photo_tag(photoid, tagname, callback)
|
||||
{
|
||||
if (tagname === ""){return}
|
||||
var url = "/photo/" + photoid;
|
||||
data = new FormData();
|
||||
data.append("add_tag", tagname);
|
||||
return post(url, data, callback);
|
||||
}
|
||||
function remove_photo_tag(photoid, tagname, callback)
|
||||
{
|
||||
if (tagname === ""){return}
|
||||
var url = "/photo/" + photoid;
|
||||
data = new FormData();
|
||||
data.append("remove_tag", tagname);
|
||||
return post(url, data, callback);
|
||||
}
|
||||
|
||||
function edit_tags(action, name, callback)
|
||||
{
|
||||
if (name === ""){return}
|
||||
var url = "/tags";
|
||||
data = new FormData();
|
||||
data.append(action, name);
|
||||
return post(url, data, callback);
|
||||
}
|
||||
function delete_tag_synonym(name, callback)
|
||||
{
|
||||
return edit_tags("delete_tag_synonym", name, callback);
|
||||
}
|
||||
function delete_tag(name, callback)
|
||||
{
|
||||
return edit_tags("delete_tag", name, callback);
|
||||
}
|
||||
function create_tag(name, callback)
|
||||
{
|
||||
return edit_tags("create_tag", name, callback);
|
||||
}
|
||||
|
||||
function post(url, data, callback)
|
||||
{
|
||||
var request = new XMLHttpRequest();
|
||||
request.answer = null;
|
||||
request.onreadystatechange = function()
|
||||
{
|
||||
if (request.readyState == 4)
|
||||
{
|
||||
var text = request.responseText;
|
||||
if (callback != null)
|
||||
{
|
||||
console.log(text);
|
||||
callback(JSON.parse(text));
|
||||
}
|
||||
}
|
||||
};
|
||||
var asynchronous = true;
|
||||
request.open("POST", url, asynchronous);
|
||||
request.send(data);
|
||||
}
|
||||
|
||||
function bind_box_to_button(box, button)
|
||||
{
|
||||
box.onkeydown=function()
|
||||
{
|
||||
if (event.keyCode == 13)
|
||||
{
|
||||
button.click();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,403 @@
|
|||
/**
|
||||
* complete.ly 1.0.0
|
||||
* MIT Licensing
|
||||
* Copyright (c) 2013 Lorenzo Puccetti
|
||||
*
|
||||
* This Software shall be used for doing good things, not bad things.
|
||||
*
|
||||
**/
|
||||
function completely(container, config) {
|
||||
config = config || {};
|
||||
config.fontSize = config.fontSize || '16px';
|
||||
config.fontFamily = config.fontFamily || 'sans-serif';
|
||||
config.promptInnerHTML = config.promptInnerHTML || '';
|
||||
config.color = config.color || '#333';
|
||||
config.hintColor = config.hintColor || '#aaa';
|
||||
config.backgroundColor = config.backgroundColor || '#fff';
|
||||
config.dropDownBorderColor = config.dropDownBorderColor || '#aaa';
|
||||
config.dropDownZIndex = config.dropDownZIndex || '100'; // to ensure we are in front of everybody
|
||||
config.dropDownOnHoverBackgroundColor = config.dropDownOnHoverBackgroundColor || '#ddd';
|
||||
|
||||
var txtInput = document.createElement('input');
|
||||
txtInput.type ='text';
|
||||
txtInput.spellcheck = false;
|
||||
txtInput.style.fontSize = config.fontSize;
|
||||
txtInput.style.fontFamily = config.fontFamily;
|
||||
txtInput.style.color = config.color;
|
||||
txtInput.style.backgroundColor = config.backgroundColor;
|
||||
txtInput.style.width = '100%';
|
||||
txtInput.style.outline = '0';
|
||||
txtInput.style.border = '0';
|
||||
txtInput.style.margin = '0';
|
||||
txtInput.style.padding = '0';
|
||||
|
||||
var txtHint = txtInput.cloneNode();
|
||||
txtHint.disabled='';
|
||||
txtHint.style.position = 'absolute';
|
||||
txtHint.style.top = '0';
|
||||
txtHint.style.left = '0';
|
||||
txtHint.style.borderColor = 'transparent';
|
||||
txtHint.style.boxShadow = 'none';
|
||||
txtHint.style.color = config.hintColor;
|
||||
|
||||
txtInput.style.backgroundColor ='transparent';
|
||||
txtInput.style.verticalAlign = 'top';
|
||||
txtInput.style.position = 'relative';
|
||||
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.style.position = 'relative';
|
||||
wrapper.style.outline = '0';
|
||||
wrapper.style.border = '0';
|
||||
wrapper.style.margin = '0';
|
||||
wrapper.style.padding = '0';
|
||||
|
||||
var prompt = document.createElement('div');
|
||||
prompt.style.position = 'absolute';
|
||||
prompt.style.outline = '0';
|
||||
prompt.style.margin = '0';
|
||||
prompt.style.padding = '0';
|
||||
prompt.style.border = '0';
|
||||
prompt.style.fontSize = config.fontSize;
|
||||
prompt.style.fontFamily = config.fontFamily;
|
||||
prompt.style.color = config.color;
|
||||
prompt.style.backgroundColor = config.backgroundColor;
|
||||
prompt.style.top = '0';
|
||||
prompt.style.left = '0';
|
||||
prompt.style.overflow = 'hidden';
|
||||
prompt.innerHTML = config.promptInnerHTML;
|
||||
prompt.style.background = 'transparent';
|
||||
if (document.body === undefined) {
|
||||
throw 'document.body is undefined. The library was wired up incorrectly.';
|
||||
}
|
||||
document.body.appendChild(prompt);
|
||||
var w = prompt.getBoundingClientRect().right; // works out the width of the prompt.
|
||||
wrapper.appendChild(prompt);
|
||||
prompt.style.visibility = 'visible';
|
||||
prompt.style.left = '-'+w+'px';
|
||||
wrapper.style.marginLeft= w+'px';
|
||||
|
||||
wrapper.appendChild(txtHint);
|
||||
wrapper.appendChild(txtInput);
|
||||
|
||||
var dropDown = document.createElement('div');
|
||||
dropDown.style.position = 'absolute';
|
||||
dropDown.style.visibility = 'hidden';
|
||||
dropDown.style.outline = '0';
|
||||
dropDown.style.margin = '0';
|
||||
dropDown.style.padding = '0';
|
||||
dropDown.style.textAlign = 'left';
|
||||
dropDown.style.fontSize = config.fontSize;
|
||||
dropDown.style.fontFamily = config.fontFamily;
|
||||
dropDown.style.backgroundColor = config.backgroundColor;
|
||||
dropDown.style.zIndex = config.dropDownZIndex;
|
||||
dropDown.style.cursor = 'default';
|
||||
dropDown.style.borderStyle = 'solid';
|
||||
dropDown.style.borderWidth = '1px';
|
||||
dropDown.style.borderColor = config.dropDownBorderColor;
|
||||
dropDown.style.overflowX= 'hidden';
|
||||
dropDown.style.whiteSpace = 'pre';
|
||||
dropDown.style.overflowY = 'scroll'; // note: this might be ugly when the scrollbar is not required. however in this way the width of the dropDown takes into account
|
||||
|
||||
|
||||
var createDropDownController = function(elem) {
|
||||
var rows = [];
|
||||
var ix = 0;
|
||||
var oldIndex = -1;
|
||||
|
||||
var onMouseOver = function() { this.style.outline = '1px solid #ddd'; }
|
||||
var onMouseOut = function() { this.style.outline = '0'; }
|
||||
var onMouseDown = function() { p.hide(); p.onmouseselection(this.__hint); }
|
||||
|
||||
var p = {
|
||||
hide : function() { elem.style.visibility = 'hidden'; },
|
||||
refresh : function(token, array) {
|
||||
elem.style.visibility = 'hidden';
|
||||
ix = 0;
|
||||
elem.innerHTML ='';
|
||||
var vph = (window.innerHeight || document.documentElement.clientHeight);
|
||||
var rect = elem.parentNode.getBoundingClientRect();
|
||||
var distanceToTop = rect.top - 6; // heuristic give 6px
|
||||
var distanceToBottom = vph - rect.bottom -6; // distance from the browser border.
|
||||
|
||||
rows = [];
|
||||
for (var i=0;i<array.length;i++) {
|
||||
if (array[i].indexOf(token)!==0) { continue; }
|
||||
var divRow =document.createElement('div');
|
||||
divRow.style.color = config.color;
|
||||
divRow.onmouseover = onMouseOver;
|
||||
divRow.onmouseout = onMouseOut;
|
||||
divRow.onmousedown = onMouseDown;
|
||||
divRow.__hint = array[i];
|
||||
divRow.innerHTML = token+'<b>'+array[i].substring(token.length)+'</b>';
|
||||
rows.push(divRow);
|
||||
elem.appendChild(divRow);
|
||||
}
|
||||
if (rows.length===0) {
|
||||
return; // nothing to show.
|
||||
}
|
||||
if (rows.length===1 && token === rows[0].__hint) {
|
||||
return; // do not show the dropDown if it has only one element which matches what we have just displayed.
|
||||
}
|
||||
|
||||
if (rows.length<2) return;
|
||||
p.highlight(0);
|
||||
|
||||
if (distanceToTop > distanceToBottom*3) { // Heuristic (only when the distance to the to top is 4 times more than distance to the bottom
|
||||
elem.style.maxHeight = distanceToTop+'px'; // we display the dropDown on the top of the input text
|
||||
elem.style.top ='';
|
||||
elem.style.bottom ='100%';
|
||||
} else {
|
||||
elem.style.top = '100%';
|
||||
elem.style.bottom = '';
|
||||
elem.style.maxHeight = distanceToBottom+'px';
|
||||
}
|
||||
elem.style.visibility = 'visible';
|
||||
},
|
||||
highlight : function(index) {
|
||||
if (oldIndex !=-1 && rows[oldIndex]) {
|
||||
rows[oldIndex].style.backgroundColor = config.backgroundColor;
|
||||
}
|
||||
rows[index].style.backgroundColor = config.dropDownOnHoverBackgroundColor; // <-- should be config
|
||||
oldIndex = index;
|
||||
},
|
||||
move : function(step) { // moves the selection either up or down (unless it's not possible) step is either +1 or -1.
|
||||
if (elem.style.visibility === 'hidden') return ''; // nothing to move if there is no dropDown. (this happens if the user hits escape and then down or up)
|
||||
if (ix+step === -1 || ix+step === rows.length) return rows[ix].__hint; // NO CIRCULAR SCROLLING.
|
||||
ix+=step;
|
||||
p.highlight(ix);
|
||||
return rows[ix].__hint;//txtShadow.value = uRows[uIndex].__hint ;
|
||||
},
|
||||
onmouseselection : function() {} // it will be overwritten.
|
||||
};
|
||||
return p;
|
||||
}
|
||||
|
||||
var dropDownController = createDropDownController(dropDown);
|
||||
|