1
0
mirror of https://github.com/aquatix/digimarks.git synced 2025-12-07 01:25:11 +01:00

89 Commits

Author SHA1 Message Date
edc1a86b75 Rolled latest changes into a version 1.0.0 release 2016-12-29 16:26:24 +01:00
698a417bac Removed workaround for fix released upstream 2016-11-07 21:29:49 +01:00
4cdea81f16 Updated MaterializeCSS and jQuery 2016-10-31 16:11:53 +01:00
0e659a4f16 Fixes for public cards 2016-09-23 13:50:49 +02:00
2cdb0ebccb Put the tag selection in a collapsible element to prevent clutter 2016-09-14 12:50:37 +02:00
9e2bdac9ee Open in new tab/window, prevent
http://davidebove.com/blog/2016/05/05/target_blank-the-vulnerability-in-your-browser/
2016-09-13 09:53:20 +02:00
5288c9fe9c Clear error message on connection error 2016-09-05 15:21:42 +02:00
0e60f8f3cb Fix for the error catching 2016-09-05 15:18:47 +02:00
5fd8db8f43 Catch connection timeouts and such 2016-09-05 15:14:11 +02:00
0894770f12 Show url domain name along with 'no title' for items without title 2016-08-29 13:49:29 +02:00
197cb776bf Helpful texts 2016-08-26 16:54:56 +02:00
c433c2c1e8 If redirect detected, button with final uri 2016-08-26 16:45:56 +02:00
b5d1377109 Changes, todos 2016-08-19 14:13:02 +02:00
85d4466de5 Filter on bookmarks with a note 2016-08-19 14:10:21 +02:00
7609becf00 More logical order of fields, tags with tags 2016-08-19 13:41:20 +02:00
a6d6ce5c13 Show stats on notes 2016-08-17 22:15:27 +02:00
e448f69f38 Count notes 2016-08-17 16:48:06 +02:00
17c36ab8fe Note about notes 2016-08-16 21:58:49 +02:00
9a15a9946c Different note icon, was same as title 2016-08-16 21:44:31 +02:00
40b7937876 Better alignment of icons in the cards 2016-08-16 21:42:10 +02:00
38155cafaa Actually show the note in title, but truncate to be safe 2016-08-16 21:34:03 +02:00
b12974fc31 Workaround for when an existing bookmark has a null note 2016-08-16 21:29:01 +02:00
d39d793460 Support for notes 2016-08-16 21:24:40 +02:00
7c24057d35 On tag filter, also use the cached tags 2016-08-15 16:03:54 +02:00
5fde397c4a Sort reverse chronologically on tag pages too 2016-08-15 16:02:12 +02:00
62117699f8 Don't show empty mobile menu on welcome page, add Home button 2016-08-15 14:34:31 +02:00
14052148f7 Prettify the mobile nav with waves and icons 2016-08-15 14:22:35 +02:00
fe44928816 Delete link/button in card is red now 2016-08-15 14:11:23 +02:00
e5c3824034 Fix for card reveal not working in Firefox 2016-08-15 10:51:59 +02:00
d8850d33f4 Test case for card not working in Firefox 2016-08-14 20:54:07 +02:00
2117c451b2 Filter text fixes 2016-08-14 14:43:11 +02:00
9abc17d517 Star/broken status filter 2016-08-14 14:39:45 +02:00
978c34fcf3 Filter on 'star' status, 'broken' status (non-http-200-OK) 2016-08-14 14:38:20 +02:00
7fdc0706bf Changes 2016-08-14 14:06:47 +02:00
c460f713ca Stats on bookmarks without http status 200 OK 2016-08-14 13:19:26 +02:00
2de7472e27 'Delete' button in action cardback 2016-08-13 20:50:24 +02:00
a6082caf0d Undo delete bookmark. Enables unescaped rendering of message 2016-08-13 20:46:09 +02:00
ac0ac849cb Whitespace good 2016-08-13 20:34:33 +02:00
390cc0137a Cleaner stats table, now with starred and deleted too 2016-08-13 20:33:17 +02:00
2e031db2a7 Stats on total public tag pages 2016-08-13 16:23:38 +02:00
1b513e98b2 Better text 2016-08-13 16:20:00 +02:00
bb258a2a68 Better total stats, now also info about nr bookmarks 2016-08-13 16:18:51 +02:00
72ad3c0d04 Total amount of tags, number of bookmarks per tag 2016-08-13 15:35:36 +02:00
78bed962d0 Delete public tag, create link 2016-08-13 14:54:11 +02:00
c70ac6d781 Way better tag overview page, with public links 2016-08-13 14:42:15 +02:00
53b96adc79 Show pointer mousepointer on tags in add/edit bookmark 2016-08-13 14:18:23 +02:00
4e86faf2aa Updated public bookmarks with new look 2016-08-13 14:14:07 +02:00
4b17721fb0 Removed favicon from card reveal; better layout in reveal 2016-08-13 14:07:17 +02:00
870418712f Merge branch 'master' of github.com:aquatix/digimarks 2016-08-13 14:03:40 +02:00
068c6b8d94 Prevent duplicate form submission; indentation fixes 2016-08-13 13:48:54 +02:00
60734c55ce Merge branch 'master' of github.com:aquatix/digimarks 2016-08-12 08:03:40 +02:00
e5c5e5f5a9 Don't have content run out of the bottom of the card 2016-08-12 08:03:23 +02:00
16ed20bd9b Better layout of Star and Warning icons 2016-08-05 20:11:05 +02:00
5840baa657 Determine of favicon is ico, decompress gzip'ed content 2016-08-05 13:30:00 +02:00
0271a9339d Use different favicon service, 60x60 icons, cleaner cards, refresh
favicons admin endpoint
2016-08-05 12:36:10 +02:00
8a28d841e3 Merge branch 'master' of github.com:aquatix/digimarks 2016-08-05 11:54:02 +02:00
04447390fc Tweaked FAB placement 2016-08-05 11:53:41 +02:00
4ee925de45 Use referrer when url encode doesn't work because of security scope 2016-08-03 21:34:44 +02:00
271f2fa4a0 Initialise with 'url' url parameter if exists, enabling bookmarklet 2016-08-03 14:14:59 +02:00
cfbab3f98b Fix for json response of single bookmark 2016-08-02 19:30:08 +02:00
62145ecfe2 Updated public tag page card design with current cards 2016-08-02 14:47:08 +02:00
42e4021ef5 Fix for link to public tag page (server part was missing) 2016-08-02 14:35:35 +02:00
e7125c07e9 Changes 2016-08-02 14:27:01 +02:00
bcc28c6a8e Feed link 2016-08-02 14:25:52 +02:00
fab1554c70 Atom feed for public tag 2016-08-02 14:16:35 +02:00
05d636fc08 json view of public tag page 2016-08-02 14:05:17 +02:00
62fcf737d0 v0.2.0 2016-08-02 13:12:07 +02:00
607a45ca8b Implemented stripping of query parameters from url 2016-08-02 13:07:24 +02:00
d5c6b751f9 Some todo's 2016-08-01 16:35:46 +02:00
50df87fb3e Change 2016-08-01 15:58:12 +02:00
3f05bc36cc 'Add' FAB on bookmarks overview page 2016-08-01 15:55:09 +02:00
ec9e7701da Changelog 2016-08-01 15:35:08 +02:00
9ab2710114 More sleek buttons 2016-08-01 15:33:45 +02:00
8f1e70f51e Removed unnecessary import 2016-08-01 15:27:00 +02:00
7864cddd32 Fail-safe way for getting tags, even if userkey is invalid 2016-08-01 15:22:12 +02:00
00c0e77c52 Attempt to get 'message' and 'tags' through a decorator 2016-08-01 15:10:52 +02:00
0111795e46 Changes 2016-07-31 14:54:17 +02:00
5fc780dac6 Typo resulting in syntax error 2016-07-31 14:45:58 +02:00
4bf7af08d5 Catch case where no url was provided. Needs more work 2016-07-31 14:41:23 +02:00
6be78117c2 Easy adding of tags by clicking/tapping tag tags 2016-07-31 14:33:31 +02:00
5f0e3bb730 Updated jQuery to 3.1.0 2016-07-31 14:02:25 +02:00
b230c5848d Update materializecss to 0.97.7 2016-07-30 14:45:14 +02:00
8bb8aff6c4 Python 2 and 3 compatible print statements 2016-07-29 14:48:59 +02:00
974fadf3d9 favicon attribution: Freepik on flaticon.com 2016-07-27 16:01:51 +02:00
062b59dc50 Changed favicon.ico to max 256px, not 512 2016-07-27 15:45:39 +02:00
6b7f7cfa16 Added favicon 2016-07-27 15:38:14 +02:00
4953f17364 No-QA hint to peewee wildcard import 2016-07-27 15:27:59 +02:00
798466f497 Fix for getting the right bookmark 2016-07-26 16:25:38 +02:00
000d5be73e TODO 2016-07-26 14:52:31 +02:00
11 changed files with 589 additions and 130 deletions

View File

@@ -1,9 +1,63 @@
## TODO
## v0.2.0 (unreleased)
- Sorting of bookmarks - Sorting of bookmarks
- Sort by title - Sort by title
- Sort by date - Sort by date
- Logging of actions
- Change tags to the MaterializeCSS tags: http://materializecss.com/chips.html
- Do calls to the API endpoint of an existing bookmark when editing properties
(for example to update tags, title and such, also to already suggest title)
- Look into compatibility with del.icio.us, so we can make use of existing browser integration
## v1.0.0
2016-12-29
- json view of public tag pages, returns all items
- feed (rss/atom) view of public tag pages, returns latest 15
- feed link on public tag page
- Support for bookmarklets
- UI tweaks
- Redesigned cards with bigger favicon. Looks cleaner
- Different favicon service with 60x60px icons
- Prevent duplicate form submission on add/edit bookmark
- Delete bookmark from bookmark card
- Undo delete link in "Bookmark has been deleted" message
- Delete public tag page
- On tags overview page:
- Show which tags have public pages, with link
- How many bookmarks each tag has
- Statistics on:
- total tags
- number of public tag pages
- total number of bookmarks
- number of starred bookmarks
- number of bookmarks with a non-OK http status
- number of deleted bookmarks
- Filter on 'star' status, 'broken' status (non-http-200-OK)
- Bookmark can have a note now
- Note icon on card with text in title (desktop browser)
- Filter on bookmarks with a note
- Show url domain name along with 'no title' for items without title
- Catch connection timeouts and such
- Open in new tab/window, prevent
http://davidebove.com/blog/2016/05/05/target_blank-the-vulnerability-in-your-browser/
- Put the tag selection in a collapsible element to prevent clutter in edit window
- Updated MaterializeCSS and jQuery
## v0.2.0
2016-08-02
- Favicon courtesy Freepik on flaticon.com
- Tag tags for easy adding of tags
- Updates to MaterializeCSS and jQuery
- Several bug- and code style fixes
- Styling tweaks
- Added 'Add bookmark' FAB to the bookmarks overview
- Option to strip parameters from url (like '?utm_source=social')
## v0.1.0 ## v0.1.0

View File

@@ -45,6 +45,16 @@ url's when wanted.
Url's are of the form https://marks.example.com/<userkey>/<action> Url's are of the form https://marks.example.com/<userkey>/<action>
Bookmarklet
~~~~~~~~~~~
To easily save a link from your browser, open its bookmark manager and create a new bookmark with as url:
.. code-block:: javascript
javascript:location.href='http://marks.example.com/1234567890abcdef/add?url='+encodeURIComponent(location.href);
Creating a new user Creating a new user
------------------- -------------------
@@ -72,6 +82,12 @@ What's new?
See the `Changelog`_. See the `Changelog`_.
Attributions
------------
'M' favicon by `Freepik`_.
.. _digimarks: https://github.com/aquatix/digimarks .. _digimarks: https://github.com/aquatix/digimarks
.. _webhook: https://en.wikipedia.org/wiki/Webhook .. _webhook: https://en.wikipedia.org/wiki/Webhook
.. |PyPI version| image:: https://img.shields.io/pypi/v/digimarks.svg .. |PyPI version| image:: https://img.shields.io/pypi/v/digimarks.svg
@@ -87,3 +103,4 @@ See the `Changelog`_.
.. _vhost for Apache2.4: https://github.com/aquatix/digimarks/blob/master/example_config/apache_vhost.conf .. _vhost for Apache2.4: https://github.com/aquatix/digimarks/blob/master/example_config/apache_vhost.conf
.. _uwsgi.ini: https://github.com/aquatix/digimarks/blob/master/example_config/uwsgi.ini .. _uwsgi.ini: https://github.com/aquatix/digimarks/blob/master/example_config/uwsgi.ini
.. _Changelog: https://github.com/aquatix/digimarks/blob/master/CHANGELOG.md .. _Changelog: https://github.com/aquatix/digimarks/blob/master/CHANGELOG.md
.. _Freepik: http://www.flaticon.com/free-icon/letter-m_2041

View File

@@ -1,4 +1,6 @@
from __future__ import print_function
import datetime import datetime
import gzip
import hashlib import hashlib
import os import os
import sys import sys
@@ -6,14 +8,15 @@ import requests
import shutil import shutil
import bs4 import bs4
from more_itertools import unique_everseen from more_itertools import unique_everseen
from urlparse import urlparse from urlparse import urlparse, urlunparse, urljoin
from utilkit import datetimeutil from utilkit import datetimeutil
from flask import Flask, abort, redirect, render_template, request, url_for from flask import Flask, abort, redirect, render_template, request, url_for, jsonify
from werkzeug.contrib.atom import AtomFeed
from flask_peewee.db import Database from flask_peewee.db import Database
#from flask_peewee.utils import get_object_or_404 #from flask_peewee.utils import get_object_or_404
from peewee import * from peewee import * # noqa
try: try:
import settings import settings
@@ -56,6 +59,23 @@ def clean_tags(tags_list):
return tags_res return tags_res
magic_dict = {
"\x1f\x8b\x08": "gz",
"\x42\x5a\x68": "bz2",
"\x50\x4b\x03\x04": "zip"
}
max_len = max(len(x) for x in magic_dict)
def file_type(filename):
with open(filename) as f:
file_start = f.read(max_len)
for magic, filetype in magic_dict.items():
if file_start.startswith(magic):
return filetype
return "no match"
class User(db.Model): class User(db.Model):
""" User account """ """ User account """
username = CharField() username = CharField()
@@ -75,6 +95,7 @@ class Bookmark(db.Model):
title = CharField(default='') title = CharField(default='')
url = CharField() url = CharField()
note = TextField(default='')
#image = CharField(default='') #image = CharField(default='')
url_hash = CharField(default='') url_hash = CharField(default='')
tags = CharField(default='') tags = CharField(default='')
@@ -84,7 +105,13 @@ class Bookmark(db.Model):
favicon = CharField(null=True) favicon = CharField(null=True)
# Status code: 200 is OK, 404 is not found, for example (showing an error) # Status code: 200 is OK, 404 is not found, for example (showing an error)
HTTP_CONNECTIONERROR = 0
HTTP_OK = 200
HTTP_MOVEDTEMPORARILY = 304
HTTP_NOTFOUND = 404
http_status = IntegerField(default=200) http_status = IntegerField(default=200)
redirect_uri = None
created_date = DateTimeField(default=datetime.datetime.now) created_date = DateTimeField(default=datetime.datetime.now)
modified_date = DateTimeField(null=True) modified_date = DateTimeField(null=True)
@@ -132,8 +159,11 @@ class Bookmark(db.Model):
def set_status_code(self): def set_status_code(self):
""" Check the HTTP status of the url, as it might not exist for example """ """ Check the HTTP status of the url, as it might not exist for example """
result = requests.head(self.url) try:
self.http_status = result.status_code result = requests.head(self.url)
self.http_status = result.status_code
except requests.ConnectionError:
self.http_status = self.HTTP_CONNECTIONERROR
return self.http_status return self.http_status
def set_favicon(self): def set_favicon(self):
@@ -141,13 +171,28 @@ class Bookmark(db.Model):
# http://codingclues.eu/2009/retrieve-the-favicon-for-any-url-thanks-to-google/ # http://codingclues.eu/2009/retrieve-the-favicon-for-any-url-thanks-to-google/
u = urlparse(self.url) u = urlparse(self.url)
domain = u.netloc domain = u.netloc
filename = os.path.join(MEDIA_ROOT, 'favicons/' + domain + '.png')
# if file exists, don't re-download it # if file exists, don't re-download it
response = requests.get('http://www.google.com/s2/favicons?domain=' + domain, stream=True) #response = requests.get('http://www.google.com/s2/favicons?domain=' + domain, stream=True)
fileextension = '.png'
meta = requests.head('http://icons.better-idea.org/icon?size=60&url=' + domain, allow_redirects=True)
if meta.url[-3:].lower() == 'ico':
fileextension = '.ico'
response = requests.get('http://icons.better-idea.org/icon?size=60&url=' + domain, stream=True)
filename = os.path.join(MEDIA_ROOT, 'favicons/' + domain + fileextension)
with open(filename, 'wb') as out_file: with open(filename, 'wb') as out_file:
shutil.copyfileobj(response.raw, out_file) shutil.copyfileobj(response.raw, out_file)
del response del response
self.favicon = domain + '.png' filetype = file_type(filename)
if filetype == 'gz':
# decompress
orig = gzip.GzipFile(filename, 'rb')
origcontent = orig.read()
orig.close()
os.remove(filename)
new = file(filename, 'wb')
new.write(origcontent)
new.close()
self.favicon = domain + fileextension
def set_tags(self, tags): def set_tags(self, tags):
""" Set tags from `tags`, strip and sort them """ """ Set tags from `tags`, strip and sort them """
@@ -155,6 +200,26 @@ class Bookmark(db.Model):
tags_clean = clean_tags(tags_split) tags_clean = clean_tags(tags_split)
self.tags = ','.join(tags_clean) self.tags = ','.join(tags_clean)
def get_redirect_uri(self):
if self.redirect_uri:
return self.redirect_uri
if self.http_status == 301 or self.http_status == 302:
result = requests.head(self.url, allow_redirects=True)
self.http_status = result.status_code
self.redirect_uri = result.url
return result.url
else:
return None
def get_uri_domain(self):
parsed = urlparse(self.url)
return parsed.hostname
@classmethod
def strip_url_params(cls, url):
parsed = urlparse(url)
return urlunparse((parsed.scheme, parsed.netloc, parsed.path, parsed.params, '', parsed.fragment))
@property @property
def tags_list(self): def tags_list(self):
""" Get the tags as a list, iterable in template """ """ Get the tags as a list, iterable in template """
@@ -196,9 +261,21 @@ def get_tags_for_user(userkey):
return clean_tags(tags) return clean_tags(tags)
def get_cached_tags(userkey):
""" Fail-safe way to get the cached tags for `userkey` """
try:
return all_tags[userkey]
except KeyError:
return []
def make_external(url):
return urljoin(request.url_root, url)
@app.errorhandler(404) @app.errorhandler(404)
def page_not_found(e): def page_not_found(e):
return render_template('404.html'), 404 return render_template('404.html', error=e), 404
@app.route('/') @app.route('/')
@@ -208,8 +285,9 @@ def index():
@app.route('/<userkey>', methods=['GET', 'POST']) @app.route('/<userkey>', methods=['GET', 'POST'])
@app.route('/<userkey>/filter/<filtermethod>', methods=['GET', 'POST'])
@app.route('/<userkey>/sort/<sortmethod>', methods=['GET', 'POST']) @app.route('/<userkey>/sort/<sortmethod>', methods=['GET', 'POST'])
def bookmarks(userkey, sortmethod = None): def bookmarks(userkey, filtermethod = None, sortmethod = None):
""" User homepage, list their bookmarks, optionally filtered and/or sorted """ """ User homepage, list their bookmarks, optionally filtered and/or sorted """
#return object_list('bookmarks.html', Bookmark.select()) #return object_list('bookmarks.html', Bookmark.select())
#user = User.select(key=userkey) #user = User.select(key=userkey)
@@ -219,14 +297,40 @@ def bookmarks(userkey, sortmethod = None):
#else: #else:
# abort(404) # abort(404)
message = request.args.get('message') message = request.args.get('message')
if request.method == 'POST': tags = get_cached_tags(userkey)
filter_on = request.form['filter']
bookmarks = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.title.contains(filter_on), filter_text = ''
if request.form:
filter_text = request.form['filter_text']
filter_starred = False
if filtermethod and filtermethod.lower() == 'starred':
filter_starred = True
filter_broken = False
if filtermethod and filtermethod.lower() == 'broken':
filter_broken = True
filter_note = False
if filtermethod and filtermethod.lower() == 'note':
filter_note = True
if filter_text:
bookmarks = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.title.contains(filter_text),
Bookmark.status == Bookmark.VISIBLE).order_by(Bookmark.created_date.desc()) Bookmark.status == Bookmark.VISIBLE).order_by(Bookmark.created_date.desc())
return render_template('bookmarks.html', bookmarks=bookmarks, userkey=userkey, tags=all_tags[userkey], filter=filter_on, message=message) elif filter_starred:
bookmarks = Bookmark.select().where(Bookmark.userkey == userkey,
Bookmark.starred == True).order_by(Bookmark.created_date.desc())
elif filter_broken:
bookmarks = Bookmark.select().where(Bookmark.userkey == userkey,
Bookmark.http_status != 200).order_by(Bookmark.created_date.desc())
elif filter_note:
bookmarks = Bookmark.select().where(Bookmark.userkey == userkey,
Bookmark.note != '').order_by(Bookmark.created_date.desc())
else: else:
bookmarks = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.status == Bookmark.VISIBLE).order_by(Bookmark.created_date.desc()) bookmarks = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.status == Bookmark.VISIBLE).order_by(Bookmark.created_date.desc())
return render_template('bookmarks.html', bookmarks=bookmarks, userkey=userkey, tags=all_tags[userkey], message=message)
return render_template('bookmarks.html', bookmarks=bookmarks, userkey=userkey, tags=tags, filter_text=filter_text, message=message)
@@ -240,8 +344,8 @@ def bookmarks(userkey, sortmethod = None):
@app.route('/<userkey>/<urlhash>/json') @app.route('/<userkey>/<urlhash>/json')
def viewbookmarkjson(userkey, urlhash): def viewbookmarkjson(userkey, urlhash):
""" Serialise bookmark to json """ """ Serialise bookmark to json """
bookmark = Bookmark.select(Bookmark.url_hash == urlhash, Bookmark.userkey == userkey, Bookmark.status == Bookmark.VISIBLE) bookmark = Bookmark.select(Bookmark.url_hash == urlhash, Bookmark.userkey == userkey, Bookmark.status == Bookmark.VISIBLE)[0]
return bookmark.to_dict() return jsonify(bookmark.to_dict())
@app.route('/<userkey>/<urlhash>') @app.route('/<userkey>/<urlhash>')
@@ -251,14 +355,25 @@ def editbookmark(userkey, urlhash):
# bookmark = getbyurlhash() # bookmark = getbyurlhash()
bookmark = Bookmark.get(Bookmark.url_hash == urlhash, Bookmark.userkey == userkey) bookmark = Bookmark.get(Bookmark.url_hash == urlhash, Bookmark.userkey == userkey)
message = request.args.get('message') message = request.args.get('message')
return render_template('edit.html', action='Edit bookmark', userkey=userkey, bookmark=bookmark, message=message, formaction='edit') tags = get_cached_tags(userkey)
if not bookmark.note:
# Workaround for when an existing bookmark has a null note
bookmark.note = ''
return render_template('edit.html', action='Edit bookmark', userkey=userkey, bookmark=bookmark, message=message, formaction='edit', tags=tags)
@app.route('/<userkey>/add') @app.route('/<userkey>/add')
def addbookmark(userkey): def addbookmark(userkey):
""" Bookmark add form """ """ Bookmark add form """
bookmark = Bookmark(title='', url='', tags='') url = request.args.get('url')
return render_template('edit.html', action='Add bookmark', userkey=userkey, bookmark=bookmark) if not url:
url = ''
if request.args.get('referrer'):
url = request.referrer
bookmark = Bookmark(title='', url=url, tags='')
message = request.args.get('message')
tags = get_cached_tags(userkey)
return render_template('edit.html', action='Add bookmark', userkey=userkey, bookmark=bookmark, tags=tags, message=message)
def updatebookmark(userkey, request, urlhash = None): def updatebookmark(userkey, request, urlhash = None):
@@ -266,9 +381,13 @@ def updatebookmark(userkey, request, urlhash = None):
title = request.form.get('title') title = request.form.get('title')
url = request.form.get('url') url = request.form.get('url')
tags = request.form.get('tags') tags = request.form.get('tags')
note = request.form.get('note')
starred = False starred = False
if request.form.get('starred'): if request.form.get('starred'):
starred = True starred = True
strip_params = False
if request.form.get('strip'):
strip_params = True
if url and not urlhash: if url and not urlhash:
# New bookmark # New bookmark
@@ -278,14 +397,20 @@ def updatebookmark(userkey, request, urlhash = None):
return redirect(url_for('editbookmark', userkey=userkey, urlhash=bookmark.url_hash, message=message)) return redirect(url_for('editbookmark', userkey=userkey, urlhash=bookmark.url_hash, message=message))
elif url: elif url:
# Existing bookmark, get from DB # Existing bookmark, get from DB
bookmark = Bookmark.get(userkey == userkey, Bookmark.url_hash == urlhash) bookmark = Bookmark.get(Bookmark.userkey == userkey, Bookmark.url_hash == urlhash)
# Editing this bookmark, set modified_date to now # Editing this bookmark, set modified_date to now
bookmark.modified_date = datetime.datetime.now() bookmark.modified_date = datetime.datetime.now()
else:
# No url was supplied, abort. @TODO: raise exception?
return None
bookmark.title = title bookmark.title = title
if strip_params:
url = Bookmark.strip_url_params(url)
bookmark.url = url bookmark.url = url
bookmark.starred = starred bookmark.starred = starred
bookmark.set_tags(tags) bookmark.set_tags(tags)
bookmark.note = note
bookmark.set_hash() bookmark.set_hash()
#bookmark.fetch_image() #bookmark.fetch_image()
if not title: if not title:
@@ -305,14 +430,17 @@ def updatebookmark(userkey, request, urlhash = None):
#@app.route('/<userkey>/adding') #@app.route('/<userkey>/adding')
def addingbookmark(userkey): def addingbookmark(userkey):
""" Add the bookmark from form submit by /add """ """ Add the bookmark from form submit by /add """
tags = get_cached_tags(userkey)
if request.method == 'POST': if request.method == 'POST':
bookmark = updatebookmark(userkey, request) bookmark = updatebookmark(userkey, request)
if not bookmark:
return redirect(url_for('addbookmark', userkey=userkey, message='No url provided', tags=tags))
if type(bookmark).__name__ == 'Response': if type(bookmark).__name__ == 'Response':
return bookmark return bookmark
all_tags[userkey] = get_tags_for_user(userkey) all_tags[userkey] = get_tags_for_user(userkey)
return redirect(url_for('editbookmark', userkey=userkey, urlhash=bookmark.url_hash)) return redirect(url_for('editbookmark', userkey=userkey, urlhash=bookmark.url_hash))
return redirect(url_for('add')) return redirect(url_for('addbookmark', userkey=userkey, tags=tags))
@app.route('/<userkey>/<urlhash>/editing', methods=['GET', 'POST']) @app.route('/<userkey>/<urlhash>/editing', methods=['GET', 'POST'])
@@ -323,7 +451,7 @@ def editingbookmark(userkey, urlhash):
bookmark = updatebookmark(userkey, request, urlhash=urlhash) bookmark = updatebookmark(userkey, request, urlhash=urlhash)
all_tags[userkey] = get_tags_for_user(userkey) all_tags[userkey] = get_tags_for_user(userkey)
return redirect(url_for('editbookmark', userkey=userkey, urlhash=bookmark.url_hash)) return redirect(url_for('editbookmark', userkey=userkey, urlhash=bookmark.url_hash))
return redirect(url_for('add')) return redirect(url_for('editbookmark', userkey=userkey, urlhash=urlhash))
@app.route('/<userkey>/<urlhash>/delete', methods=['GET', 'POST']) @app.route('/<userkey>/<urlhash>/delete', methods=['GET', 'POST'])
@@ -333,7 +461,17 @@ def deletingbookmark(userkey, urlhash):
query.execute() query.execute()
query = Bookmark.update(deleted_date = datetime.datetime.now()).where(Bookmark.userkey==userkey, Bookmark.url_hash==urlhash) query = Bookmark.update(deleted_date = datetime.datetime.now()).where(Bookmark.userkey==userkey, Bookmark.url_hash==urlhash)
query.execute() query.execute()
message = 'Bookmark deleted' message = 'Bookmark deleted. <a href="{}">Undo deletion</a>'.format(url_for('undeletebookmark', userkey=userkey, urlhash=urlhash))
all_tags[userkey] = get_tags_for_user(userkey)
return redirect(url_for('bookmarks', userkey=userkey, message=message))
@app.route('/<userkey>/<urlhash>/undelete')
def undeletebookmark(userkey, urlhash):
""" Undo deletion of the bookmark identified by urlhash """
query = Bookmark.update(status=Bookmark.VISIBLE).where(Bookmark.userkey==userkey, Bookmark.url_hash==urlhash)
query.execute()
message = 'Bookmark restored'
all_tags[userkey] = get_tags_for_user(userkey) all_tags[userkey] = get_tags_for_user(userkey)
return redirect(url_for('bookmarks', userkey=userkey, message=message)) return redirect(url_for('bookmarks', userkey=userkey, message=message))
@@ -341,16 +479,34 @@ def deletingbookmark(userkey, urlhash):
@app.route('/<userkey>/tags') @app.route('/<userkey>/tags')
def tags(userkey): def tags(userkey):
""" Overview of all tags used by user """ """ Overview of all tags used by user """
tags = get_tags_for_user(userkey) tags = get_cached_tags(userkey)
print tags #publictags = PublicTag.select().where(Bookmark.userkey == userkey)
return render_template('tags.html', tags=tags, userkey=userkey) alltags = []
for tag in tags:
try:
publictag = PublicTag.get(PublicTag.userkey == userkey, PublicTag.tag == tag)
except PublicTag.DoesNotExist:
publictag = None
total = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.tags.contains(tag), Bookmark.status == Bookmark.VISIBLE).count()
alltags.append({'tag': tag, 'publictag': publictag, 'total': total})
totaltags = len(alltags)
totalbookmarks = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.status == Bookmark.VISIBLE).count()
totalpublic = PublicTag.select().where(PublicTag.userkey == userkey).count()
totalstarred = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.starred == True).count()
totaldeleted = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.status == Bookmark.DELETED).count()
totalnotes = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.note != '').count()
totalhttperrorstatus = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.http_status != 200).count()
return render_template('tags.html', tags=alltags, totaltags=totaltags, totalpublic=totalpublic, totalbookmarks=totalbookmarks,
totaldeleted=totaldeleted, totalstarred=totalstarred, totalhttperrorstatus=totalhttperrorstatus,
totalnotes=totalnotes, userkey=userkey)
@app.route('/<userkey>/tag/<tag>') @app.route('/<userkey>/tag/<tag>')
def tag(userkey, tag): def tag(userkey, tag):
""" Overview of all bookmarks with a certain tag """ """ Overview of all bookmarks with a certain tag """
bookmarks = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.tags.contains(tag), Bookmark.status == Bookmark.VISIBLE) bookmarks = Bookmark.select().where(Bookmark.userkey == userkey, Bookmark.tags.contains(tag), Bookmark.status == Bookmark.VISIBLE).order_by(Bookmark.created_date.desc())
tags = get_tags_for_user(userkey) tags = get_cached_tags(userkey)
pageheader = 'tag: ' + tag pageheader = 'tag: ' + tag
message = request.args.get('message') message = request.args.get('message')
@@ -366,10 +522,46 @@ def tag(userkey, tag):
def publictag(tagkey): def publictag(tagkey):
""" Read-only overview of the bookmarks in the userkey/tag of this PublicTag """ """ Read-only overview of the bookmarks in the userkey/tag of this PublicTag """
#this_tag = get_object_or_404(PublicTag.select().where(PublicTag.tagkey == tagkey)) #this_tag = get_object_or_404(PublicTag.select().where(PublicTag.tagkey == tagkey))
try:
this_tag = PublicTag.get(PublicTag.tagkey == tagkey)
bookmarks = Bookmark.select().where(Bookmark.userkey == this_tag.userkey, Bookmark.tags.contains(this_tag.tag), Bookmark.status == Bookmark.VISIBLE).order_by(Bookmark.created_date.desc())
return render_template('publicbookmarks.html', bookmarks=bookmarks, tag=tag, action=this_tag.tag, tagkey=tagkey)
except PublicTag.DoesNotExist:
abort(404)
@app.route('/pub/<tagkey>/json')
def publictagjson(tagkey):
""" json representation of the Read-only overview of the bookmarks in the userkey/tag of this PublicTag """
try: try:
this_tag = PublicTag.get(PublicTag.tagkey == tagkey) this_tag = PublicTag.get(PublicTag.tagkey == tagkey)
bookmarks = Bookmark.select().where(Bookmark.userkey == this_tag.userkey, Bookmark.tags.contains(this_tag.tag), Bookmark.status == Bookmark.VISIBLE) bookmarks = Bookmark.select().where(Bookmark.userkey == this_tag.userkey, Bookmark.tags.contains(this_tag.tag), Bookmark.status == Bookmark.VISIBLE)
return render_template('publicbookmarks.html', bookmarks=bookmarks, tag=tag, action=this_tag.tag) result = {'count': len(bookmarks), 'items': []}
for bookmark in bookmarks:
result['items'].append(bookmark.to_dict())
return jsonify(result)
except PublicTag.DoesNotExist:
abort(404)
@app.route('/pub/<tagkey>/feed')
def publictagfeed(tagkey):
""" rss/atom representation of the Read-only overview of the bookmarks in the userkey/tag of this PublicTag """
try:
this_tag = PublicTag.get(PublicTag.tagkey == tagkey)
bookmarks = Bookmark.select().where(Bookmark.userkey == this_tag.userkey, Bookmark.tags.contains(this_tag.tag), Bookmark.status == Bookmark.VISIBLE).limit(15)
feed = AtomFeed(this_tag.tag, feed_url=request.url, url=make_external(url_for('publictag', tagkey=tagkey)))
for bookmark in bookmarks:
updated_date = bookmark.modified_date
if not bookmark.modified_date:
updated_date = bookmark.created_date
feed.add(bookmark.title,
content_type='html',
author='digimarks',
url=bookmark.url,
updated=updated_date,
published=bookmark.created_date)
return feed.get_response()
except PublicTag.DoesNotExist: except PublicTag.DoesNotExist:
abort(404) abort(404)
@@ -399,6 +591,14 @@ def addpublictag(userkey, tag):
return redirect(url_for('tag', userkey=userkey, tag=tag, message=message)) return redirect(url_for('tag', userkey=userkey, tag=tag, message=message))
@app.route('/<userkey>/<tag>/removepublic/<tagkey>', methods=['GET', 'POST'])
def removepublictag(userkey, tag, tagkey):
q = PublicTag.delete().where(PublicTag.userkey == userkey, PublicTag.tag == tag, PublicTag.tagkey == tagkey)
q.execute()
message = 'Public link deleted'
return redirect(url_for('tag', userkey=userkey, tag=tag, message=message))
@app.route('/<systemkey>/adduser') @app.route('/<systemkey>/adduser')
def adduser(systemkey): def adduser(systemkey):
""" Add user endpoint, convenience """ """ Add user endpoint, convenience """
@@ -413,18 +613,36 @@ def adduser(systemkey):
abort(404) abort(404)
# Initialise @app.route('/<systemkey>/refreshfavicons')
# create the bookmark, user and public tag tables if they do not exist def refreshfavicons(systemkey):
""" Add user endpoint, convenience """
if systemkey == settings.SYSTEMKEY:
bookmarks = Bookmark.select()
for bookmark in bookmarks:
if bookmark.favicon:
try:
filename = os.path.join(MEDIA_ROOT, 'favicons/' + bookmark.favicon)
os.remove(filename)
except OSError as e:
print(e)
bookmark.set_favicon()
return redirect('/')
else:
abort(404)
# Initialisation == create the bookmark, user and public tag tables if they do not exist
Bookmark.create_table(True) Bookmark.create_table(True)
User.create_table(True) User.create_table(True)
PublicTag.create_table(True) PublicTag.create_table(True)
users = User.select() users = User.select()
print 'Current user keys:' print('Current user keys:')
for user in users: for user in users:
all_tags[user.key] = get_tags_for_user(user.key) all_tags[user.key] = get_tags_for_user(user.key)
print user.key print(user.key)
# Run when called standalone
if __name__ == '__main__': if __name__ == '__main__':
# run the application # run the application
app.run(port=9999, debug=True) app.run(port=9999, debug=True)

View File

@@ -26,7 +26,7 @@ setup(
# third part for minor release # third part for minor release
# second when api changes # second when api changes
# first when it becomes stable someday # first when it becomes stable someday
version='0.1.0', version='1.0.0',
author='Michiel Scholten', author='Michiel Scholten',
author_email='michiel@diginaut.net', author_email='michiel@diginaut.net',

View File

@@ -44,10 +44,17 @@
.card.tiny .card.tiny
{ {
height: 140px; height: 140px;
overflow: hidden;
} }
.card.tiny .card-title
{
font-size: 18px;
}
.card .card-reveal .digimark-card-header, .card .card-reveal .digimark-card-header,
.card .digimark-card-header.activator .card .digimark-card-header.activator,
.chip.clickable
{ {
cursor: pointer; cursor: pointer;
/*display: block;*/ /*display: block;*/
@@ -57,3 +64,14 @@
{ {
padding-top: 10px; padding-top: 10px;
} }
.card-image i
{
padding: 5px 0 0 15px;
}
.card.horizontal .card-image img.favicon
{
height: 60px;
width: 60px;
}

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@@ -5,6 +5,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}"/>
<!-- Chrome, Firefox OS and Opera --> <!-- Chrome, Firefox OS and Opera -->
<meta name="theme-color" content="#2e7d32" /> <meta name="theme-color" content="#2e7d32" />
@@ -16,8 +17,9 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Roboto+Mono&subset=latin,latin-ext' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Roboto+Mono&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css" type="text/css" rel="stylesheet" media="screen,projection"/> <link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.8/css/materialize.min.css" type="text/css" rel="stylesheet" media="screen,projection"/>
<link href="{{ url_for('static', filename='css/digimarks.css') }}" type="text/css" rel="stylesheet" media="screen,projection"/> <link href="{{ url_for('static', filename='css/digimarks.css') }}" type="text/css" rel="stylesheet" media="screen,projection"/>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
</head> </head>
<body class="grey lighten-4"> <body class="grey lighten-4">
<nav class="green darken-3" role="navigation"> <nav class="green darken-3" role="navigation">
@@ -29,13 +31,14 @@
{% endif %} {% endif %}
</ul> </ul>
{% if userkey %}
<ul id="nav-mobile" class="side-nav"> <ul id="nav-mobile" class="side-nav">
{% if userkey %} <li><a class="waves-effect" href="{{ url_for('bookmarks', userkey=userkey) }}"><i class="material-icons">turned_in</i>Home</a></li>
<li><a href="{{ url_for('tags', userkey=userkey) }}">Tags</a></li> <li><a class="waves-effect" href="{{ url_for('tags', userkey=userkey) }}"><i class="material-icons">label</i>Tags</a></li>
<li><a href="{{ url_for('addbookmark', userkey=userkey) }}">Add bookmark</a></li> <li><a class="waves-effect" href="{{ url_for('addbookmark', userkey=userkey) }}"><i class="material-icons">add</i>Add bookmark</a></li>
{% endif %}
</ul> </ul>
<a href="#" data-activates="nav-mobile" class="button-collapse"><i class="material-icons">menu</i></a> <a href="#" data-activates="nav-mobile" class="button-collapse"><i class="material-icons">menu</i></a>
{% endif %}
</div> </div>
</nav> </nav>
<div class="section no-pad-bot" id="index-banner"> <div class="section no-pad-bot" id="index-banner">
@@ -53,8 +56,7 @@
</div> </div>
<!-- Scripts --> <!-- Scripts -->
<script src="https://code.jquery.com/jquery-2.2.3.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.8/js/materialize.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/js/materialize.min.js"></script>
<script src="{{ url_for('static', filename='js/init.js') }}"></script> <script src="{{ url_for('static', filename='js/init.js') }}"></script>
</body> </body>

View File

@@ -25,7 +25,7 @@
<div class="col s12"> <div class="col s12">
<div class="card-panel orange lighten-2"> <div class="card-panel orange lighten-2">
<span class="white-text"> <span class="white-text">
{{ message }} {{ message|safe }}
</span> </span>
</div> </div>
</div> </div>
@@ -35,24 +35,39 @@
<div class="row"> <div class="row">
<form action="{{ url_for('bookmarks', userkey=userkey) }}" method="POST"> <form action="{{ url_for('bookmarks', userkey=userkey) }}" method="POST">
<div class="input-field col l10 m10 s8"> <div class="input-field col l10 m10 s8">
<input placeholder="filter" type="text" name="filter" id="filter" value="{{ filter }}" class="validate" /> <input placeholder="search text" type="text" name="filter_text" id="filter_text" value="{{ filter_text }}" class="validate" />
</div> </div>
<div class="input-field col l2 m2 s4"> <div class="input-field col l2 m2 s4">
<p class="left-align"><button class="btn btn-large waves-effect waves-light" type="submit" name="submit">Filter</button></p> <p class="left-align"><button class="btn waves-effect waves-light" type="submit" name="submit">Filter</button></p>
</div> </div>
</form> </form>
</div> </div>
{% if tags %} {% if tags %}
<div class="row"> <div class="row">
<p> <div class="col s12">
{% for tag in tags %} <ul class="collapsible" data-collapsible="expandable">
<div class="chip"> <li>
<a href="{{ url_for('tag', userkey=userkey, tag=tag) }}">{{ tag }}</a> <div class="collapsible-header"><i class="material-icons">label</i>Filter on star/problem/comment/tag</div>
<div class="collapsible-body" style="padding: 10px;">
<div class="chip">
<a href="{{ url_for('bookmarks', userkey=userkey, filtermethod='starred') }}"><i class="tiny material-icons yellow-text">star</i></a>
</div>
<div class="chip">
<a href="{{ url_for('bookmarks', userkey=userkey, filtermethod='broken') }}"><i class="tiny material-icons red-text">report_problem</i></a>
</div>
<div class="chip">
<a href="{{ url_for('bookmarks', userkey=userkey, filtermethod='note') }}"><i class="tiny material-icons">comment</i></a>
</div>
{% for tag in tags %}
<div class="chip">
<a href="{{ url_for('tag', userkey=userkey, tag=tag) }}">{{ tag }}</a>
</div>
{% endfor %}
</li>
</ul>
</div> </div>
{% endfor %}
</p>
</div> </div>
{% endif %} {% endif %}
@@ -68,43 +83,47 @@
<p>{{ bookmark.created_date.strftime("%m/%d/%Y %H:%M") }}</p> <p>{{ bookmark.created_date.strftime("%m/%d/%Y %H:%M") }}</p>
</div> </div>
#} #}
<div class="card tiny green darken-3"> <div class="card horizontal tiny green darken-3">
<div class="card-content white-text"> <div class="card-image">
<span class="digimark-card-header activator"> {% if bookmark.favicon %}
{% for tag in bookmark.tags_list %} <div><img src="{{ url_for('static', filename='favicons/' + bookmark.favicon) }}" class="favicon" /></div>
<div class="chip"> {% endif %}
<a href="{{ url_for('tag', userkey=userkey, tag=tag) }}">{{ tag }}</a> {% if bookmark.http_status != 200 and bookmark.http_status != 304 %}
</div> <div><i class="small material-icons red-text" title="HTTP status {{ bookmark.http_status }}">report_problem</i></div>
{% endfor %} {% endif %}
<i class="material-icons right">more_vert</i> {% if bookmark.starred == True %}
</span> <div><i class="small material-icons yellow-text">star</i></div>
<div class="valign-wrapper digimark-card-content"> {% endif %}
<div class="valign"> {% if bookmark.note %}
{% if bookmark.http_status != 200 %} <div><i class="small material-icons white-text" title="{{ bookmark.note|truncate(100) }}">comment</i></div>
<i class="tiny material-icons" title="HTTP status {{ bookmark.http_status }}">report_problem</i> {% endif %}
{% endif %} </div>
{% if bookmark.favicon %} <div class="card-stacked">
<img src="{{ url_for('static', filename='favicons/' + bookmark.favicon) }}" />&nbsp; <div class="card-content white-text">
{% endif %} <span class="digimark-card-header activator">
<a href="{{ bookmark.url }}" title="{{ bookmark.url }}" rel="noreferrer"> {% for tag in bookmark.tags_list %}
{% if bookmark.starred == True %} <div class="chip">
<i class="tiny material-icons yellow-text">star</i> <a href="{{ url_for('tag', userkey=userkey, tag=tag) }}">{{ tag }}</a>
{% endif %} </div>
{% if bookmark.title %} {% endfor %}
{{ bookmark.title }} <i class="material-icons right">more_vert</i>
{% else %} </span>
[ no title ] <div class="digimark-card-content">
{% endif %} <a href="{{ bookmark.url }}" title="{{ bookmark.url }}" rel="noreferrer noopener" target="_blank">
</a></div> {% if bookmark.title %}
{{ bookmark.title }}
{% else %}
{{ bookmark.get_uri_domain() }} (no title)
{% endif %}
</a>
</div> </div>
</div>
</div> </div>
<div class="card-reveal green darken-3"> <div class="card-reveal green darken-3">
<span class="card-title white-text valign-wrapper">Added @ {{ bookmark.created_date.strftime('%Y-%m-%d %H:%M') }}<i class="material-icons right">close</i></span> <span class="card-title white-text">Added @ {{ bookmark.created_date.strftime('%Y-%m-%d %H:%M') }}<i class="material-icons right">close</i></span>
<div class="white-text valign"> <div class="white-text" style="padding-top: 10px;">
{% if bookmark.favicon %}
<img src="{{ url_for('static', filename='favicons/' + bookmark.favicon) }}" />&nbsp;&nbsp;&nbsp;
{% endif %}
<a href="{{ url_for('editbookmark', userkey=userkey, urlhash=bookmark.url_hash) }}" style="padding: 3px"><i class="tiny material-icons">mode_edit</i> EDIT</a> <a href="{{ url_for('editbookmark', userkey=userkey, urlhash=bookmark.url_hash) }}" style="padding: 3px"><i class="tiny material-icons">mode_edit</i> EDIT</a>
<a href="{{ url_for('deletingbookmark', userkey=userkey, urlhash=bookmark.url_hash) }}" style="padding: 3px" class="red-text"><i class="tiny material-icons">delete</i> DELETE</a>
</div> </div>
</div> </div>
</div> </div>
@@ -118,4 +137,10 @@
</div> </div>
#} #}
</div> </div>
<div class="fixed-action-btn" style="bottom: 20px; right: 20px;">
<a class="btn-floating btn-large red" href="{{ url_for('addbookmark', userkey=userkey) }}">
<i class="large material-icons">add</i>
</a>
</div>
{% endblock %} {% endblock %}

View File

@@ -3,13 +3,17 @@
{% block pageheader %}{{ action }}{% endblock %} {% block pageheader %}{{ action }}{% endblock %}
{% block pagecontent %} {% block pagecontent %}
{% if bookmark.http_status != 200 %} {% if bookmark.http_status != 200 and bookmark.http_status != 304 %}
<div class="row"> <div class="row">
<div class="col s12"> <div class="col s12">
<div class="card-panel red darken-1"> <div class="card-panel red darken-1">
<span class="white-text"> <span class="white-text">
{% if bookmark.http_status == 404 %} {% if bookmark.http_status == 404 %}
<i class="material-icons">report_problem</i>&nbsp;&nbsp;URL not found (404), broken/outdated link? <i class="material-icons">report_problem</i>&nbsp;&nbsp;URL not found (404), broken/outdated link?
{% elif bookmark.http_status == 302 %}
<i class="material-icons">report_problem</i>&nbsp;&nbsp;HTTP status (302), moved temporarily. Use button for new target
{% elif bookmark.http_status == bookmark.HTTP_CONNECTIONERROR %}
<i class="material-icons">report_problem</i>&nbsp;&nbsp;Connection error, server might have been offline at the time of last edit
{% else %} {% else %}
<i class="material-icons">report_problem</i>&nbsp;&nbsp;HTTP status {{ bookmark.http_status }} <i class="material-icons">report_problem</i>&nbsp;&nbsp;HTTP status {{ bookmark.http_status }}
{% endif %} {% endif %}
@@ -31,17 +35,16 @@
</div> </div>
{% endif %} {% endif %}
{% if formaction and formaction == 'edit' %} {% if formaction and formaction == 'edit' %}
<form action="{{ url_for('editingbookmark', userkey=userkey, urlhash=bookmark.url_hash) }}" method="POST"> <form class="digimark" action="{{ url_for('editingbookmark', userkey=userkey, urlhash=bookmark.url_hash) }}" method="POST">
{% else %} {% else %}
<form action="{{ url_for('addingbookmark', userkey=userkey) }}" method="POST"> <form class="digimark" action="{{ url_for('addingbookmark', userkey=userkey) }}" method="POST">
{% endif %} {% endif %}
<div class="row">
<div class="row">
<div class="input-field col s12"> <div class="input-field col s12">
<i class="material-icons prefix">description</i> <i class="material-icons prefix">description</i>
<input placeholder="title" type="text" name="title" id="title" value="{{ bookmark.title }}" class="validate" /> <input placeholder="title (leave empty for autofetch)" type="text" name="title" id="title" value="{{ bookmark.title }}" class="validate" />
<label for="title">Title</label> <label for="title">Title</label>
</div> </div>
@@ -49,6 +52,25 @@
<i class="material-icons prefix">turned_in</i> <i class="material-icons prefix">turned_in</i>
<input placeholder="url" type="text" name="url" id="url" value="{{ bookmark.url }}" class="validate" /> <input placeholder="url" type="text" name="url" id="url" value="{{ bookmark.url }}" class="validate" />
<label for="url">URL</label> <label for="url">URL</label>
{% if bookmark.get_redirect_uri() %}
<div>
<a class="waves-effect waves-light btn" id="btn_urlupdate"><i class="material-icons left">turned_in</i>{{ bookmark.get_redirect_uri() }}</a>
</div>
<script type="text/javascript">
$(function () {
$('#btn_urlupdate').on('click', function () {
var text = $('#url');
text.val('{{ bookmark.get_redirect_uri() }}');
});
});
</script>
{% endif %}
</div>
<div class="input-field col s12">
<i class="material-icons prefix">comment</i>
<input placeholder="note" type="text" name="note" id="note" value="{{ bookmark.note }}" class="validate" />
<label for="note">Note</label>
</div> </div>
<div class="input-field col s12"> <div class="input-field col s12">
@@ -56,6 +78,36 @@
<input placeholder="tags, divided by comma's" type="text" name="tags" id="tags" value="{{ bookmark.tags }}" class="validate" /> <input placeholder="tags, divided by comma's" type="text" name="tags" id="tags" value="{{ bookmark.tags }}" class="validate" />
<label for="tags">Tags</label> <label for="tags">Tags</label>
</div> </div>
</div>
{% if tags %}
<div class="row">
<div class="col s12">
<ul class="collapsible" data-collapsible="expandable">
<li>
<div class="collapsible-header"><i class="material-icons">label</i>Existing tags</div>
<div class="collapsible-body" style="padding: 10px;">
{% for tag in tags %}
<div class="chip clickable" id="tag_{{ tag }}">
{{ tag }}
</div>
{% endfor %}
</div>
</li>
</ul>
</div>
{% for tag in tags %}
<script type="text/javascript">
$(function () {
$('#tag_{{ tag }}').on('click', function () {
var text = $('#tags');
text.val(text.val() + ', {{ tag }}');
});
});
</script>
{% endfor %}
</div>
{% endif %}
<div class="row">
<div class="input-field col s12"> <div class="input-field col s12">
{#<i class="material-icons prefix">star</i>#} {#<i class="material-icons prefix">star</i>#}
@@ -65,7 +117,7 @@
<div class="input-field col s12"> <div class="input-field col s12">
<input type="checkbox" name="strip" id="strip" /> <input type="checkbox" name="strip" id="strip" />
<label for="strip">Strip parameters from url</label> <label for="strip">Strip parameters from url (like <em>?utm_source=social</em> - can break the link!)</label>
</div> </div>
{% if bookmark.url_hash %} {% if bookmark.url_hash %}
@@ -96,15 +148,25 @@
{% endif %} {% endif %}
<div class="input-field col l2 m3 s4"> <div class="input-field col l2 m3 s4">
<p class="left-align"><button class="btn btn-large waves-effect waves-light" type="submit" name="submit">Save <i class="material-icons right">send</i></button></p> <p class="left-align"><button class="btn waves-effect waves-light" type="submit" name="submit" id="submit">Save <i class="material-icons right">send</i></button></p>
</div> </div>
</form>
{% if bookmark.url_hash %} {% if bookmark.url_hash %}
</form>
<div class="input-field col l2 m3 s4"> <div class="input-field col l2 m3 s4">
<form action="{{ url_for('deletingbookmark', userkey=userkey, urlhash=bookmark.url_hash) }}" method="POST"> <form action="{{ url_for('deletingbookmark', userkey=userkey, urlhash=bookmark.url_hash) }}" method="POST">
<p class="left-align"><button class="btn btn-large waves-effect waves-light" type="submit" name="delete">Delete <i class="material-icons right">delete</i></button></p> <p class="left-align"><button class="btn waves-effect waves-light" type="submit" name="delete">Delete <i class="material-icons right">delete</i></button></p>
</form> </form>
</div> </div>
</div>
{% else %}
</div>
</form>
{% endif %} {% endif %}
</div>
<script>
$(function() {
console.log('woei');
$('form.digimark').on('submit',function(){$("#submit").prop("disabled", true); return true;})
});
</script>
{% endblock %} {% endblock %}

View File

@@ -18,36 +18,48 @@
</div> </div>
{% endif %} {% endif %}
<div class="row">
<div class="col s12">
<a href="{{ url_for('publictagfeed', tagkey=tagkey) }}"><i class="material-icons tiny">rss_feed</i> feed</a>
</div>
</div>
<div class="row"> <div class="row">
{% for bookmark in bookmarks %} {% for bookmark in bookmarks %}
<div class="col s12 m6 l4"> <div class="col s12 m6 l4">
<div class="card green darken-3"> <div class="card horizontal tiny green darken-3">
<div class="card-content white-text"> <div class="card-image">
<span class="card-title activator"><i class="material-icons right">more_vert</i></span> {% if bookmark.favicon %}
{% if bookmark.http_status != 200 %} <div><img src="{{ url_for('static', filename='favicons/' + bookmark.favicon) }}" class="favicon" /></div>
<i class="material-icons" title="HTTP status {{ bookmark.http_status }}">report_problem</i> {% endif %}
{% endif %} {% if bookmark.http_status != 200 %}
{% if bookmark.favicon %} <i class="small material-icons red-text" title="HTTP status {{ bookmark.http_status }}">report_problem</i><br />
<img src="{{ url_for('static', filename='favicons/' + bookmark.favicon) }}" />&nbsp;&nbsp;&nbsp; {% endif %}
{% endif %} {% if bookmark.starred == True %}
<p><a href="{{ bookmark.url }}" title="{{ bookmark.url }}" rel="noreferrer"> <i class="small material-icons yellow-text">star</i>
{% if bookmark.starred == True %} {% endif %}
<i class="material-icons">star</i> {% if bookmark.note %}
{% endif %} <div><i class="small material-icons white-text" title="{{ bookmark.note|truncate(100) }}">comment</i></div>
{% if bookmark.title %} {% endif %}
{{ bookmark.title }} </div>
{% else %} <div class="card-stacked">
[ no title ] <div class="card-content white-text">
{% endif %} <span class="digimark-card-header activator">
</a></p> <i class="material-icons right">more_vert</i>
</span>
<div class="digimark-card-content">
<a href="{{ bookmark.url }}" title="{{ bookmark.url }}" rel="noreferrer noopener" target="_blank">
{% if bookmark.title %}
{{ bookmark.title }}
{% else %}
{{ bookmark.get_uri_domain() }} (no title)
{% endif %}
</a>
</div>
</div>
</div> </div>
<div class="card-reveal green darken-3"> <div class="card-reveal green darken-3">
<span class="card-title white-text">Added @ {{ bookmark.created_date.strftime('%Y-%m-%d %H:%M') }}<i class="material-icons right">close</i></span> <span class="card-title white-text">Added @ {{ bookmark.created_date.strftime('%Y-%m-%d %H:%M') }}<i class="material-icons right">close</i></span>
<div class="white-text">
{% if bookmark.favicon %}
<img src="{{ url_for('static', filename='favicons/' + bookmark.favicon) }}" />&nbsp;&nbsp;&nbsp;
{% endif %}
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -4,11 +4,62 @@
{% block pagecontent %} {% block pagecontent %}
<div class="row"> <div class="row">
{% for tag in tags %} <div class="col s12">
<div class="chip"> <table class="centered">
<a href="{{ url_for('tag', userkey=userkey, tag=tag) }}">{{ tag }}</a> <thead>
</div> <tr>
{% endfor %} <th><i class="material-icons" title="Unique labels">label</i></th>
<th><i class="material-icons green-text" title="Public tag pages">present_to_all</i></th>
<th><i class="material-icons" title="Total bookmarks">turned_in</i></th>
<th><i class="material-icons" title="Bookmarks with notes">comment</i></th>
<th><i class="material-icons yellow-text" title="Starred bookmarks">star</i></th>
<th><i class="material-icons orange-text" title="HTTP status is not 200 OK">warning</i></th>
<th><i class="material-icons red-text" title="Deleted bookmarks">delete</i></th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ totaltags }}</td>
<td>{{ totalpublic }}</td>
<td>{{ totalbookmarks }}</td>
<td>{{ totalnotes }}</td>
<td>{{ totalstarred }}</td>
<td>{{ totalhttperrorstatus }}</td>
<td>{{ totaldeleted }}</td>
</tr>
</tbody>
</table>
<br /><br />
<table>
<thead>
<tr>
<th>Tag</th>
<th>Public link</th>
<th>Number of bookmarks</th>
</tr>
</thead>
<tbody>
{% for tag in tags %}
<tr>
<td>
<a href="{{ url_for('tag', userkey=userkey, tag=tag['tag']) }}">{{ tag['tag'] }}</a>
</td>
<td>
{% if tag['publictag'] %}
<a href="{{ url_for('publictag', tagkey=tag['publictag'].tagkey) }}">Public link</a> (<a href="{{ url_for('removepublictag', tag=tag['tag'], tagkey=tag['publictag'].tagkey, userkey=userkey) }}">Delete</a> <i class="tiny material-icons red-text">warning</i>)
{% else %}
<a href="{{ url_for('addpublictag', userkey=userkey, tag=tag['tag']) }}">Create</a>
{% endif %}
</td>
<td>
{{ tag['total'] }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div> </div>
{% endblock %} {% endblock %}