CSS : Modify stylesheet due to modifying XHTML;

Python : Improve editing of tags;
Python : Fix error upon no results on Atom;
SQLite : Add triggers to count instances of entries;
SQLite : Add triggers to delete entries
SQLite : Add triggers to delete tags;
XSLT   : Remove control characters in favour of nbsp entity;
XSLT   : Improve links and design;
XHTML  : Add a new document about folksonomy;
XHTML  : Add a new template for URL;
XHTML  : Add indicators.
This commit is contained in:
Schimon Jehudah, Adv. 2024-08-28 15:03:19 +03:00
parent 164b4d67d4
commit a53c3a3436
34 changed files with 926 additions and 100 deletions

288
blasta.py
View file

@ -491,6 +491,18 @@ class HttpInstance:
response.headers["Content-Type"] = "application/xhtml+xml" response.headers["Content-Type"] = "application/xhtml+xml"
return response return response
@self.app.get('/help/about/folksonomy')
def help_about_folksonomies_get(request: Request):
jabber_id = Utilities.is_jid_matches_to_session(accounts, sessions, request)
template_file = 'folksonomy.xhtml'
template_dict = {
'request' : request,
'jabber_id' : jabber_id,
'journal' : journal}
response = templates.TemplateResponse(template_file, template_dict)
response.headers["Content-Type"] = "application/xhtml+xml"
return response
@self.app.get('/help/about/ideas') @self.app.get('/help/about/ideas')
def help_about_ideas_get(request: Request): def help_about_ideas_get(request: Request):
jabber_id = Utilities.is_jid_matches_to_session(accounts, sessions, request) jabber_id = Utilities.is_jid_matches_to_session(accounts, sessions, request)
@ -849,6 +861,11 @@ class HttpInstance:
entries_count = SQLite.get_entries_count_by_jid(db_file, jid) entries_count = SQLite.get_entries_count_by_jid(db_file, jid)
for tag, instances in SQLite.get_30_tags_by_jid(db_file, jid, index_first): for tag, instances in SQLite.get_30_tags_by_jid(db_file, jid, index_first):
tags_dict[tag] = instances tags_dict[tag] = instances
if not entries_database:
message = 'Blasta system message » Error: No entries were found.'
description = 'No results'
path = 'error'
return result_post(request, jabber_id, description, message, path)
if entries_count: if entries_count:
entries = [] entries = []
for entry in entries_database: for entry in entries_database:
@ -856,6 +873,8 @@ class HttpInstance:
for tag in SQLite.get_tags_by_entry_id(db_file, entry[0]): for tag in SQLite.get_tags_by_entry_id(db_file, entry[0]):
tags_sorted.append(tag[0]) tags_sorted.append(tag[0])
entry_jid = SQLite.get_jid_by_jid_id(db_file, entry[5]) entry_jid = SQLite.get_jid_by_jid_id(db_file, entry[5])
url_hash = Utilities.hash_url_to_md5(entry[2])
instances = SQLite.get_entry_instances_by_url_hash(db_file, url_hash)
entries.append( entries.append(
{'title' : entry[3], {'title' : entry[3],
'link' : entry[2], 'link' : entry[2],
@ -863,10 +882,10 @@ class HttpInstance:
'published' : entry[6], 'published' : entry[6],
'updated' : entry[7], 'updated' : entry[7],
'tags' : tags_sorted, 'tags' : tags_sorted,
'url_hash' : Utilities.hash_url_to_md5(entry[2]), #entry[1] 'url_hash' : url_hash,
'jid' : entry_jid, 'jid' : entry_jid,
'name' : entry_jid, # jid.split('@')[0] if '@' in jid else jid, 'name' : entry_jid, # jid.split('@')[0] if '@' in jid else jid,
'instances' : entry[8]}) 'instances' : instances})
for entry in entries: for entry in entries:
try: try:
date_iso = entry['published'] date_iso = entry['published']
@ -985,6 +1004,7 @@ class HttpInstance:
'jid': jid, 'jid': jid,
'journal': journal, 'journal': journal,
'message': message, 'message': message,
'node_type': node_type,
'page_next': page_next, 'page_next': page_next,
'page_prev': page_prev, 'page_prev': page_prev,
'pager' : True, 'pager' : True,
@ -1095,6 +1115,11 @@ class HttpInstance:
entries_database = SQLite.get_entries_recent(db_file, index_first) entries_database = SQLite.get_entries_recent(db_file, index_first)
tags_of_entries = SQLite.get_30_tags_by_entries_recent(db_file, index_first) tags_of_entries = SQLite.get_30_tags_by_entries_recent(db_file, index_first)
entries_count = SQLite.get_entries_count(db_file) entries_count = SQLite.get_entries_count(db_file)
if not entries_database:
message = 'Blasta system message » Error: No entries were found.'
description = 'No results'
path = 'error'
return result_post(request, jabber_id, description, message, path)
tags_dict = {} tags_dict = {}
#for tag, instances in SQLite.get_tags_30(db_file): #for tag, instances in SQLite.get_tags_30(db_file):
for tag, instances in tags_of_entries: for tag, instances in tags_of_entries:
@ -1105,6 +1130,8 @@ class HttpInstance:
for tag in SQLite.get_tags_by_entry_id(db_file, entry[0]): for tag in SQLite.get_tags_by_entry_id(db_file, entry[0]):
tags_sorted.append(tag[0]) tags_sorted.append(tag[0])
jid = SQLite.get_jid_by_jid_id(db_file, entry[5]) jid = SQLite.get_jid_by_jid_id(db_file, entry[5])
url_hash = Utilities.hash_url_to_md5(entry[2])
instances = SQLite.get_entry_instances_by_url_hash(db_file, url_hash)
entries.append( entries.append(
{'title' : entry[3], {'title' : entry[3],
'link' : entry[2], 'link' : entry[2],
@ -1112,10 +1139,10 @@ class HttpInstance:
'published' : entry[6], 'published' : entry[6],
'updated' : entry[7], 'updated' : entry[7],
'tags' : tags_sorted, 'tags' : tags_sorted,
'url_hash' : Utilities.hash_url_to_md5(entry[2]), #entry[1] 'url_hash' : url_hash, #entry[1]
'jid' : jid, 'jid' : jid,
'name' : jid, # jid.split('@')[0] if '@' in jid else jid, 'name' : jid, # jid.split('@')[0] if '@' in jid else jid,
'instances' : entry[8]}) 'instances' : instances})
for entry in entries: for entry in entries:
try: try:
date_iso = entry['published'] date_iso = entry['published']
@ -1139,7 +1166,6 @@ class HttpInstance:
message = ('Welcome to Blasta, an XMPP PubSub oriented social ' message = ('Welcome to Blasta, an XMPP PubSub oriented social '
'bookmarks manager for organizing online content.') 'bookmarks manager for organizing online content.')
message_link = {'href' : '/help/about', 'text' : 'Learn more'} message_link = {'href' : '/help/about', 'text' : 'Learn more'}
template_file = 'browse.xhtml'
template_dict = { template_dict = {
'request' : request, 'request' : request,
'description' : description, 'description' : description,
@ -1151,6 +1177,7 @@ class HttpInstance:
'node_id' : node_id, 'node_id' : node_id,
'page_next' : page_next, 'page_next' : page_next,
'page_prev' : page_prev, 'page_prev' : page_prev,
'page_type' : page_type,
'pager' : True, 'pager' : True,
'param_query' : param_query, 'param_query' : param_query,
'param_tags' : param_tags, 'param_tags' : param_tags,
@ -1159,6 +1186,8 @@ class HttpInstance:
'syndicate' : syndicate, 'syndicate' : syndicate,
'tags' : tags_dict} 'tags' : tags_dict}
if param_mode == 'feed': if param_mode == 'feed':
# NOTE Consider scheme "feed" in order to prompt news
# reader 'feed://' + request.url.netloc + request.url.path
template_file = 'browse.atom' template_file = 'browse.atom'
response = templates.TemplateResponse(template_file, template_dict) response = templates.TemplateResponse(template_file, template_dict)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/xml"
@ -1507,7 +1536,6 @@ class HttpInstance:
'jid' : jabber_id, 'jid' : jabber_id,
'name' : name, 'name' : name,
'instances' : instances} 'instances' : instances}
#message = 'Discover new links and see who shares them'
xmpp_instance = accounts[jabber_id] xmpp_instance = accounts[jabber_id]
payload = Syndication.create_rfc4287_entry(entry_new) payload = Syndication.create_rfc4287_entry(entry_new)
iq = await XmppPubsub.publish_node_item( iq = await XmppPubsub.publish_node_item(
@ -1556,12 +1584,19 @@ class HttpInstance:
param_tags = request.query_params.get('tags', '') param_tags = request.query_params.get('tags', '')
param_summary = request.query_params.get('summary', '') param_summary = request.query_params.get('summary', '')
path = 'save' path = 'save'
if request.query_params:
message = message_link = None
else:
message = 'For greater ease, you migh want to try our'
message_link = {'href' : '/help/utilities#buttons', 'text' : 'bookmarklets'}
template_file = 'edit.xhtml' template_file = 'edit.xhtml'
template_dict = { template_dict = {
'request' : request, 'request' : request,
'description' : description, 'description' : description,
'jabber_id' : jabber_id, 'jabber_id' : jabber_id,
'journal' : journal, 'journal' : journal,
'message' : message,
'message_link' : message_link,
'path' : path, 'path' : path,
'routine' : routine, 'routine' : routine,
'summary' : param_summary, 'summary' : param_summary,
@ -1873,7 +1908,7 @@ class HttpInstance:
entries.append(entry) entries.append(entry)
tags_list = {} tags_list = {}
tags_and_instances = SQLite.get_tags_and_instances_by_url_hash(db_file, url_hash) tags_and_instances = SQLite.get_tags_and_instances_by_url_hash(db_file, url_hash)
for tag, instances in tags_and_instances: tags_list[tag] = instances for tag, tag_instances in tags_and_instances: tags_list[tag] = tag_instances
else: # NOTE Is it possible to activate this else statement? Consider removal. else: # NOTE Is it possible to activate this else statement? Consider removal.
# https://fastapi.tiangolo.com/tutorial/handling-errors/ # https://fastapi.tiangolo.com/tutorial/handling-errors/
#raise HTTPException(status_code=404, detail="Item not found") #raise HTTPException(status_code=404, detail="Item not found")
@ -1890,8 +1925,9 @@ class HttpInstance:
tags_sorted.append(tag[0]) tags_sorted.append(tag[0])
tags_list = {} tags_list = {}
tags_and_instances = SQLite.get_tags_and_instances_by_entry_id(db_file, entry[0]) tags_and_instances = SQLite.get_tags_and_instances_by_entry_id(db_file, entry[0])
for tag, instances in tags_and_instances: tags_list[tag] = instances for tag, tag_instances in tags_and_instances: tags_list[tag] = tag_instances
jid = SQLite.get_jid_by_jid_id(db_file, entry[5]) jid = SQLite.get_jid_by_jid_id(db_file, entry[5])
instances = SQLite.get_entry_instances_by_url_hash(db_file, url_hash)
entries.append( entries.append(
{'title' : entry[3], {'title' : entry[3],
'link' : entry[2], 'link' : entry[2],
@ -1900,10 +1936,10 @@ class HttpInstance:
'published_mod' : Utilities.convert_iso8601_to_readable(entry[6]), 'published_mod' : Utilities.convert_iso8601_to_readable(entry[6]),
'updated' : entry[7], 'updated' : entry[7],
'tags' : tags_sorted, 'tags' : tags_sorted,
'url_hash' : entry[1], # Utilities.hash_url_to_md5(entry[2]) 'url_hash' : url_hash,
'jid' : jid, 'jid' : jid,
'name' : jid, # jid.split('@')[0] if '@' in jid else jid, 'name' : jid, # jid.split('@')[0] if '@' in jid else jid,
'instances' : entry[8]}) 'instances' : instances})
# message = 'XMPP system message » {}.'.format(iq) # message = 'XMPP system message » {}.'.format(iq)
# if iq == 'Node not found': # if iq == 'Node not found':
# description = 'An error has occurred' # description = 'An error has occurred'
@ -1925,8 +1961,9 @@ class HttpInstance:
tags_sorted.append(tag[0]) tags_sorted.append(tag[0])
tags_list = {} tags_list = {}
tags_and_instances = SQLite.get_tags_and_instances_by_entry_id(db_file, entry[0]) tags_and_instances = SQLite.get_tags_and_instances_by_entry_id(db_file, entry[0])
for tag, instances in tags_and_instances: tags_list[tag] = instances for tag, tag_instances in tags_and_instances: tags_list[tag] = tag_instances
jid = SQLite.get_jid_by_jid_id(db_file, entry[5]) jid = SQLite.get_jid_by_jid_id(db_file, entry[5])
instances = SQLite.get_entry_instances_by_url_hash(db_file, url_hash)
entries.append( entries.append(
{'title' : entry[3], {'title' : entry[3],
'link' : entry[2], 'link' : entry[2],
@ -1935,10 +1972,10 @@ class HttpInstance:
'published_mod' : Utilities.convert_iso8601_to_readable(entry[6]), 'published_mod' : Utilities.convert_iso8601_to_readable(entry[6]),
'updated' : entry[7], 'updated' : entry[7],
'tags' : tags_sorted, 'tags' : tags_sorted,
'url_hash' : entry[1], # Utilities.hash_url_to_md5(entry[2]) 'url_hash' : url_hash,
'jid' : jid, 'jid' : jid,
'name' : jid, # jid.split('@')[0] if '@' in jid else jid, 'name' : jid, # jid.split('@')[0] if '@' in jid else jid,
'instances' : entry[8]}) 'instances' : instances})
else: else:
# https://fastapi.tiangolo.com/tutorial/handling-errors/ # https://fastapi.tiangolo.com/tutorial/handling-errors/
#raise HTTPException(status_code=404, detail="Item not found") #raise HTTPException(status_code=404, detail="Item not found")
@ -1946,9 +1983,21 @@ class HttpInstance:
description = 'The requested bookmark does not exist' description = 'The requested bookmark does not exist'
path = 'error' path = 'error'
return result_post(request, jabber_id, description, message, path) return result_post(request, jabber_id, description, message, path)
message = 'Information for URL {}'.format(entries[0]['link']) # entry[2] message = 'Information for URI {}'.format(entries[0]['link']) # entry[2]
description = 'Discover new links and see who shares them' if instances > 1:
template_file = 'browse.xhtml' description = 'Discover new resources and see who shares them'
template_file = 'people.xhtml'
people_list = {}
jids_and_tags = SQLite.get_jids_and_tags_by_url_hash(db_file, url_hash)
for jid, tag in jids_and_tags:
if jid in people_list and isinstance(people_list[jid], list):
people_list[jid].append(tag)
else:
people_list[jid] = [tag]
else:
people_list = None
description = 'Resource properties'
template_file = 'browse.xhtml'
template_dict = { template_dict = {
'request' : request, 'request' : request,
'description' : description, 'description' : description,
@ -1960,6 +2009,7 @@ class HttpInstance:
'node_id' : node_id, 'node_id' : node_id,
'param_hash' : param_hash, 'param_hash' : param_hash,
'path' : path, 'path' : path,
'people' : people_list,
'pubsub_jid' : jabber_id_pubsub, 'pubsub_jid' : jabber_id_pubsub,
'syndicate' : syndicate, 'syndicate' : syndicate,
'tags' : tags_list} 'tags' : tags_list}
@ -2069,16 +2119,19 @@ class HttpInstance:
tags_invalid = [] tags_invalid = []
#tags_list_new = tags.split(',') #tags_list_new = tags.split(',')
tags_list_new = tags_new tags_list_new = tags_new
tags_list_old = tags_old.split(',') tags_list_old = tags_old.split(', ')
for tag in tags_list_old: for tag in tags_list_old:
tag_trim = tag.strip()
if tag not in tags_list_new: if tag not in tags_list_new:
tags_invalid.append(tag) tags_invalid.append(tag_trim)
for tag in tags_list_new: for tag in tags_list_new:
if tag not in tags_list_old: if tag:
tags_valid.append(tag) tag_trim = tag.strip()
# FIXME Variable tags_valid is not in use. if tag_trim not in tags_list_old:
# NOTE Variable tags_valid might not be needed. See function associate_entries_tags_jids. tags_valid.append(tag_trim)
await SQLite.delete_combination_row_by_entry_id_and_tag_id_and_jid_id(db_file, url_hash, tags_invalid, jabber_id) # FIXME Variable tags_valid is not in use.
# NOTE Variable tags_valid might not be needed. See function associate_entries_tags_jids.
entry['tags'] = tags_valid
await SQLite.add_tags(db_file, [entry]) await SQLite.add_tags(db_file, [entry])
# Slow (high I/O) # Slow (high I/O)
entry_id = SQLite.get_entry_id_by_url_hash(db_file, url_hash) entry_id = SQLite.get_entry_id_by_url_hash(db_file, url_hash)
@ -2089,6 +2142,17 @@ class HttpInstance:
# await SQLite.associate_entries_tags_jids(db_file, entry) # await SQLite.associate_entries_tags_jids(db_file, entry)
else: else:
await SQLite.associate_entries_tags_jids(db_file, entry) await SQLite.associate_entries_tags_jids(db_file, entry)
print('tags_new')
print(tags_new)
print('tags_old')
print(tags_old)
print('tags_valid')
print(tags_valid)
print('tags_invalid')
print(tags_invalid)
print(url_hash)
print(jabber_id)
await SQLite.delete_combination_row_by_url_hash_and_tag_and_jid(db_file, url_hash, tags_invalid, jabber_id)
# Entry for HTML # Entry for HTML
entry['published_mod'] = Utilities.convert_iso8601_to_readable(published) entry['published_mod'] = Utilities.convert_iso8601_to_readable(published)
entry['updated_mod'] = Utilities.convert_iso8601_to_readable(timestamp) entry['updated_mod'] = Utilities.convert_iso8601_to_readable(timestamp)
@ -2250,7 +2314,7 @@ class HttpInstance:
# Remove the item association from database # Remove the item association from database
await SQLite.delete_combination_row_by_jid_and_url_hash(db_file, url_hash, jabber_id) await SQLite.delete_combination_row_by_jid_and_url_hash(db_file, url_hash, jabber_id)
#await SQLite.delete_combination_row_by_entry_id_and_tag_id_and_jid_id(db_file, url_hash, entry['tags'], jabber_id) #await SQLite.delete_combination_row_by_url_hash_and_tag_and_jid(db_file, url_hash, entry['tags'], jabber_id)
# Remove the item from cache # Remove the item from cache
Data.remove_item_from_cache(jabber_id, node_type, url_hash) Data.remove_item_from_cache(jabber_id, node_type, url_hash)
@ -2323,6 +2387,8 @@ class HttpInstance:
description = 'Edit an existing bookmark' description = 'Edit an existing bookmark'
entry = Syndication.extract_items(item_payload) entry = Syndication.extract_items(item_payload)
entry['instances'] = SQLite.get_entry_instances_by_url_hash(db_file, url_hash) entry['instances'] = SQLite.get_entry_instances_by_url_hash(db_file, url_hash)
print(jabber_id)
print(entry['tags'])
else: else:
# TODO Consider redirect to path /save (function save_get) # TODO Consider redirect to path /save (function save_get)
# NOTE This seems to be the best to do, albeit, perhaps the pathname should be /save instead of /url/hash/edit. # NOTE This seems to be the best to do, albeit, perhaps the pathname should be /save instead of /url/hash/edit.
@ -2338,8 +2404,8 @@ class HttpInstance:
'summary' : result[4], 'summary' : result[4],
'published' : result[6], 'published' : result[6],
'updated' : result[7], 'updated' : result[7],
'tags' : tags_sorted, 'tags' : tags_sorted}
'instances' : result[8]} #'instances' : result[8],
#'jid' = jabber_id, #'jid' = jabber_id,
#'name' : name, #'name' : name,
#'url_hash' : url_hash #'url_hash' : url_hash
@ -2347,6 +2413,14 @@ class HttpInstance:
entry['jid'] = jabber_id entry['jid'] = jabber_id
entry['name'] = name entry['name'] = name
entry['url_hash'] = url_hash entry['url_hash'] = url_hash
else:
message = 'XMPP system message » {}.'.format(iq)
if iq == 'Node not found':
description = 'An error has occurred'
else:
description = 'An unknown error has occurred'
path = 'error'
return result_post(request, jabber_id, description, message, path)
template_file = 'edit.xhtml' template_file = 'edit.xhtml'
template_dict = { template_dict = {
'request' : request, 'request' : request,
@ -2543,6 +2617,64 @@ class SQLite:
); );
""" """
) )
sql_trigger_instances_entry_decrease = (
"""
CREATE TRIGGER instances_entry_decrease
AFTER DELETE ON combination_entries_tags_jids
FOR EACH ROW
BEGIN
UPDATE main_entries
SET instances = (
SELECT COUNT(DISTINCT jid_id)
FROM combination_entries_tags_jids
WHERE entry_id = OLD.entry_id
)
WHERE id = OLD.entry_id;
END;
"""
)
sql_trigger_instances_entry_increase = (
"""
CREATE TRIGGER instances_entry_increase
AFTER INSERT ON combination_entries_tags_jids
FOR EACH ROW
BEGIN
UPDATE main_entries
SET instances = (
SELECT COUNT(DISTINCT jid_id)
FROM combination_entries_tags_jids
WHERE entry_id = NEW.entry_id
)
WHERE id = NEW.entry_id;
END;
"""
)
sql_trigger_instances_entry_update = (
"""
CREATE TRIGGER instances_entry_update
AFTER UPDATE ON combination_entries_tags_jids
FOR EACH ROW
BEGIN
-- Decrease instances for the old tag_id
UPDATE main_entries
SET instances = (
SELECT COUNT(DISTINCT jid_id)
FROM combination_entries_tags_jids
WHERE entry_id = OLD.entry_id
)
WHERE id = OLD.entry_id;
-- Increase instances for the new tag_id
UPDATE main_entries
SET instances = (
SELECT COUNT(DISTINCT jid_id)
FROM combination_entries_tags_jids
WHERE entry_id = NEW.entry_id
)
WHERE id = NEW.entry_id;
END;
"""
)
sql_trigger_instances_tag_decrease = ( sql_trigger_instances_tag_decrease = (
""" """
CREATE TRIGGER instances_tag_decrease CREATE TRIGGER instances_tag_decrease
@ -2643,6 +2775,17 @@ class SQLite:
END; END;
""" """
) )
sql_trigger_entry_remove = (
"""
CREATE TRIGGER entry_remove
AFTER UPDATE ON main_entries
FOR EACH ROW
WHEN NEW.instances < 1
BEGIN
DELETE FROM main_entries WHERE id = OLD.id;
END;
"""
)
sql_trigger_jid_count_increase = ( sql_trigger_jid_count_increase = (
""" """
CREATE TRIGGER jid_count_increase CREATE TRIGGER jid_count_increase
@ -2727,6 +2870,17 @@ class SQLite:
END; END;
""" """
) )
sql_trigger_tag_remove = (
"""
CREATE TRIGGER tag_remove
AFTER UPDATE ON main_tags
FOR EACH ROW
WHEN NEW.instances < 1
BEGIN
DELETE FROM main_tags WHERE id = OLD.id;
END;
"""
)
cur = conn.cursor() cur = conn.cursor()
cur.execute(sql_table_main_entries) cur.execute(sql_table_main_entries)
cur.execute(sql_table_main_jids) cur.execute(sql_table_main_jids)
@ -2736,18 +2890,23 @@ class SQLite:
cur.execute(sql_table_authorization_entries_jids) cur.execute(sql_table_authorization_entries_jids)
cur.execute(sql_table_report_entries) cur.execute(sql_table_report_entries)
cur.execute(sql_table_report_jids) cur.execute(sql_table_report_jids)
cur.execute(sql_trigger_instances_entry_decrease)
cur.execute(sql_trigger_instances_entry_increase)
cur.execute(sql_trigger_instances_entry_update)
cur.execute(sql_trigger_instances_tag_decrease) cur.execute(sql_trigger_instances_tag_decrease)
cur.execute(sql_trigger_instances_tag_increase) cur.execute(sql_trigger_instances_tag_increase)
cur.execute(sql_trigger_instances_tag_update) cur.execute(sql_trigger_instances_tag_update)
cur.execute(sql_trigger_entry_count_increase) cur.execute(sql_trigger_entry_count_increase)
cur.execute(sql_trigger_entry_count_decrease) cur.execute(sql_trigger_entry_count_decrease)
cur.execute(sql_trigger_entry_count_update) cur.execute(sql_trigger_entry_count_update)
cur.execute(sql_trigger_entry_remove)
cur.execute(sql_trigger_jid_count_increase) cur.execute(sql_trigger_jid_count_increase)
cur.execute(sql_trigger_jid_count_decrease) cur.execute(sql_trigger_jid_count_decrease)
cur.execute(sql_trigger_jid_count_update) cur.execute(sql_trigger_jid_count_update)
cur.execute(sql_trigger_tag_count_increase) cur.execute(sql_trigger_tag_count_increase)
cur.execute(sql_trigger_tag_count_decrease) cur.execute(sql_trigger_tag_count_decrease)
cur.execute(sql_trigger_tag_count_update) cur.execute(sql_trigger_tag_count_update)
cur.execute(sql_trigger_tag_remove)
def add_statistics(db_file): def add_statistics(db_file):
""" """
@ -3022,7 +3181,7 @@ class SQLite:
result = cur.execute(sql, par).fetchone() result = cur.execute(sql, par).fetchone()
return result[0] if result and len(result) == 1 else result return result[0] if result and len(result) == 1 else result
async def delete_combination_row_by_entry_id_and_tag_id_and_jid_id(db_file, url_hash, tags, jid): async def delete_combination_row_by_url_hash_and_tag_and_jid(db_file, url_hash, tags, jid):
""" """
Delete a row by a given entry ID and a given Jabber ID and given tags. Delete a row by a given entry ID and a given Jabber ID and given tags.
@ -3042,8 +3201,8 @@ class SQLite:
None. None.
""" """
function_name = sys._getframe().f_code.co_name function_name = sys._getframe().f_code.co_name
# logger.debug('{}: db_file: {} entry_id: {} tag_id: {} jid_id: {}' # logger.debug('{}: db_file: {} url_hash: {} tag_id: {} jid_id: {}'
# .format(function_name, db_file, entry_id, tag_id, jid_id)) # .format(function_name, db_file, url_hash, tag_id, jid_id))
sql = ( sql = (
""" """
DELETE DELETE
@ -3168,6 +3327,77 @@ class SQLite:
result = cur.execute(sql, par).fetchall() result = cur.execute(sql, par).fetchall()
return result return result
def get_jids_and_tags_by_entry_id(db_file, entry_id):
"""
Get JIDs and tags by a given ID entry.
Parameters
----------
db_file : str
Path to database file.
entry_id : str
An ID of an entry.
Returns
-------
result : tuple
JIDs and tags.
"""
function_name = sys._getframe().f_code.co_name
# logger.debug('{}: db_file: {} index_first: {}'
# .format(function_name, db_file, index_first))
sql = (
"""
SELECT main_jids.jid, main_tags.tag
FROM main_tags
INNER JOIN combination_entries_tags_jids ON main_tags.id = combination_entries_tags_jids.tag_id
INNER JOIN main_jids ON main_jids.id = combination_entries_tags_jids.jid_id
WHERE combination_entries_tags_jids.entry_id = ?
ORDER BY main_tags.instances DESC;
"""
)
par = (entry_id,)
with SQLite.create_connection(db_file) as conn:
cur = conn.cursor()
result = cur.execute(sql, par).fetchall()
return result
def get_jids_and_tags_by_url_hash(db_file, url_hash):
"""
Get JIDs and tags by a given URI hash.
Parameters
----------
db_file : str
Path to database file.
url_hash : str
A URL hash of an entry.
Returns
-------
result : tuple
JIDs and tags.
"""
function_name = sys._getframe().f_code.co_name
# logger.debug('{}: db_file: {} index_first: {}'
# .format(function_name, db_file, index_first))
sql = (
"""
SELECT main_jids.jid, main_tags.tag
FROM main_tags
INNER JOIN combination_entries_tags_jids ON main_tags.id = combination_entries_tags_jids.tag_id
INNER JOIN main_jids ON main_jids.id = combination_entries_tags_jids.jid_id
INNER JOIN main_entries ON main_entries.id = combination_entries_tags_jids.entry_id
WHERE main_entries.url_hash = ?
ORDER BY main_tags.instances DESC;
"""
)
par = (url_hash,)
with SQLite.create_connection(db_file) as conn:
cur = conn.cursor()
result = cur.execute(sql, par).fetchall()
return result
def get_tag_id_by_tag(db_file, tag): def get_tag_id_by_tag(db_file, tag):
""" """
Get a tag ID by a given tag. Get a tag ID by a given tag.

View file

@ -1,9 +1,9 @@
# Contact # Contact
[contacts] [contacts]
email = "" email = ""
xmpp = "" xmpp = "sch@pimux.de"
mix = "" mix = ""
muc = "" muc = "syndication@conference.movim.eu"
irc_channel = "#blasta" irc_channel = "#blasta"
irc_server = "" irc_server = ""

View file

@ -115,15 +115,15 @@ h3, h4, h5 {
padding: 0 0.4em; padding: 0 0.4em;
} }
.title:first-child { #posts > dd:first-child {
margin-block-start: 2.22em; margin-block-start: 2.22em;
} }
.title { #posts > dd {
margin-block-start: 1.33em; margin-block-start: 1.33em;
} }
.title > h4 { article > h4 {
display: inline; display: inline;
} }
/* /*

View file

@ -11,8 +11,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:output <xsl:output
method = 'html' method = 'html'
indent = 'yes' indent = 'yes' />
omit-xml-decleration='no' />
<!-- set page metadata --> <!-- set page metadata -->
<xsl:template name='metadata'> <xsl:template name='metadata'>
@ -67,7 +66,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<!-- feed title --> <!-- feed title -->
<h1> <h1>
<img src='/graphic/blasta_syndicate.svg'/> <img src='/graphic/blasta_syndicate.svg'/>
&#8202; &#160;
<xsl:value-of select='atom:title'/> <xsl:value-of select='atom:title'/>
</h1> </h1>
<dl id='navigation'> <dl id='navigation'>
@ -77,8 +76,18 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<dd> <dd>
<xsl:element name='a'> <xsl:element name='a'>
<xsl:attribute name='href'> <xsl:attribute name='href'>
<xsl:value-of select='atom:link'/> <xsl:value-of select='atom:link[@rel="alternate" and @type="text/html"]/@href'/>
</xsl:attribute> </xsl:attribute>
<xsl:attribute name='title'>Return to Blasta.</xsl:attribute>
Return
</xsl:element>
</dd>
<dd>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='atom:link[@rel="self"]/@href'/>
</xsl:attribute>
<xsl:attribute name='download'>blasta_atom_syndication_feed.atom</xsl:attribute>
<xsl:attribute name='id'>follow</xsl:attribute> <xsl:attribute name='id'>follow</xsl:attribute>
<xsl:attribute name='title'>Subscribe to updates with a news reader software.</xsl:attribute> <xsl:attribute name='title'>Subscribe to updates with a news reader software.</xsl:attribute>
Follow Follow
@ -102,6 +111,16 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
SubToMe SubToMe
</a> </a>
</dd> </dd>
<dd>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='atom:link[@rel="alternate" and @type="x-scheme-handler/xmpp"]/@href'/>
</xsl:attribute>
<xsl:attribute name='id'>subscribe</xsl:attribute>
<xsl:attribute name='title'>Subscribe to updates with an XMPP software.</xsl:attribute>
Subscribe
</xsl:element>
</dd>
<dd> <dd>
<a href='/help/about/xmpp' <a href='/help/about/xmpp'
title='Of the benefits of XMPP.'> title='Of the benefits of XMPP.'>
@ -127,7 +146,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<div> <div>
<h3> <h3>
<img src='/graphic/syndicate.svg' height='22' width='22'/> <img src='/graphic/syndicate.svg' height='22' width='22'/>
&#8202; &#160;
<xsl:value-of select='atom:subtitle'/> <xsl:value-of select='atom:subtitle'/>
</h3> </h3>
<!-- xsl:for-each select='atom:entry[position() &lt;21]' --> <!-- xsl:for-each select='atom:entry[position() &lt;21]' -->
@ -164,21 +183,20 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</a> </a>
</h4> </h4>
<h5 class='related'> <h5 class='related'>
&#8202;
<img alt='💡' <img alt='💡'
height='16' height='16'
src='/graphic/xmpp.svg' src='/graphic/xmpp.svg'
width='16' /> width='16' />
&#8202; &#160;
<a href='/help/about/xmpp/pubsub'>XMPP</a> <a href='/help/about/xmpp/pubsub'>/help/about/xmpp/pubsub</a>
&#8202; &#160;
<img alt='⚛' <img alt='⚛'
class='enlarge' class='enlarge'
height='16' height='16'
src='/graphic/syndicate.svg' src='/graphic/syndicate.svg'
width='16' /> width='16' />
&#8202; &#160;
<a href='/help/syndication'>Syndication</a> <a href='/help/syndication'>/help/syndication</a>
</h5> </h5>
<p> <p>
<b>Congratulations!</b> You have reached to the final <b>Congratulations!</b> You have reached to the final
@ -196,15 +214,15 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
the ones that would fit to you best! the ones that would fit to you best!
</p> </p>
<div class='details'> <div class='details'>
<a href='?tags=filetype:atom'>filetype:atom </a> <a href='?mode=feed&amp;tags=filetype:atom'>filetype:atom </a>
<a href='?tags=filetype:rss'>filetype:rss </a> <a href='?mode=feed&amp;tags=filetype:rss'>filetype:rss </a>
<a href='?tags=filetype:xml'>filetype:xml </a> <a href='?mode=feed&amp;tags=filetype:xml'>filetype:xml </a>
<a href='?tags=software:akregator'>software:akregator </a> <a href='?mode=feed&amp;tags=software:akregator'>software:akregator </a>
<a href='?tags=software:fraidycat'>software:fraidycat </a> <a href='?mode=feed&amp;tags=software:fraidycat'>software:fraidycat </a>
<a href='?tags=software:liferea'>software:liferea </a> <a href='?mode=feed&amp;tags=software:liferea'>software:liferea </a>
<a href='?tags=software:otter-browser'>software:otter-browser </a> <a href='?mode=feed&amp;tags=software:otter-browser'>software:otter-browser </a>
<a href='?tags=software:raven-reader'>software:raven-reader </a> <a href='?mode=feed&amp;tags=software:raven-reader'>software:raven-reader </a>
<a href='?tags=niche:syndication'>niche:syndication</a> <a href='?mode=feed&amp;tags=niche:syndication'>niche:syndication</a>
<h5 class='updated'>2024-08-22T17:09:04+03:00</h5> <h5 class='updated'>2024-08-22T17:09:04+03:00</h5>
<h5 class='author'> <h5 class='author'>
<a href='/jid/sch@pimux.de'>sch@pimux.de</a> <a href='/jid/sch@pimux.de'>sch@pimux.de</a>
@ -252,13 +270,13 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</xsl:attribute> </xsl:attribute>
PubSub PubSub
</xsl:element> </xsl:element>
&#8203;&#8202; &#160;
💬 💬
<xsl:element name='a'> <xsl:element name='a'>
<xsl:attribute name='href'> <xsl:attribute name='href'>
<xsl:value-of select='atom:link[@rel="related" and @type="text/html"]/@href'/> <xsl:value-of select='atom:link[@rel="related" and @type="text/html"]/@href'/>
</xsl:attribute> </xsl:attribute>
People Folksonomy
</xsl:element> </xsl:element>
</h5> </h5>
<!-- entry content --> <!-- entry content -->
@ -271,11 +289,11 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:for-each select='atom:category'> <xsl:for-each select='atom:category'>
<xsl:element name='a'> <xsl:element name='a'>
<xsl:attribute name='href'> <xsl:attribute name='href'>
?tags=<xsl:value-of select='@term'/> ?mode=feed&amp;tags=<xsl:value-of select='@term'/>
</xsl:attribute> </xsl:attribute>
<xsl:value-of select='@term'/> <xsl:value-of select='@term'/>
</xsl:element> </xsl:element>
&#8202; &#160;
</xsl:for-each> </xsl:for-each>
<h5 class='date'> <h5 class='date'>
<!-- entry date --> <!-- entry date -->
@ -362,21 +380,20 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</a> </a>
</h4> </h4>
<h5 class='related'> <h5 class='related'>
&#8202;
<img alt='💡' <img alt='💡'
height='16' height='16'
src='/graphic/xmpp.svg' src='/graphic/xmpp.svg'
width='16' /> width='16' />
&#8202; &#160;
<a href='/help/about/xmpp/pubsub'>XMPP</a> <a href='/help/about/xmpp/pubsub'>About XMPP</a>
&#8202; &#160;
<img alt='⚛' <img alt='⚛'
class='enlarge' class='enlarge'
height='16' height='16'
src='/graphic/syndicate.svg' src='/graphic/syndicate.svg'
width='16' /> width='16' />
&#8202; &#160;
<a href='/help/syndication'>Syndication</a> <a href='/help/syndication'>About Syndication</a>
</h5> </h5>
<p> <p>
This is a concise introduction to the syndication This is a concise introduction to the syndication
@ -407,17 +424,17 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
Language Family (XSL). Language Family (XSL).
</p> </p>
<div class='details'> <div class='details'>
<a href='?tags=brand:atompub'>brand:atompub </a> <a href='?mode=feed&amp;tags=brand:atompub'>brand:atompub </a>
<a href='?tags=brand:atomsub'>brand:atomsub </a> <a href='?mode=feed&amp;tags=brand:atomsub'>brand:atomsub </a>
<a href='?tags=brand:pubsub'>brand:pubsub </a> <a href='?mode=feed&amp;tags=brand:pubsub'>brand:pubsub </a>
<a href='?tags=code:xslt'>code:xslt </a> <a href='?mode=feed&amp;tags=code:xslt'>code:xslt </a>
<a href='?tags=filetype:atom'>filetype:atom </a> <a href='?mode=feed&amp;tags=filetype:atom'>filetype:atom </a>
<a href='?tags=filetype:rss'>filetype:rss </a> <a href='?mode=feed&amp;tags=filetype:rss'>filetype:rss </a>
<a href='?tags=filetype:xml'>filetype:xml </a> <a href='?mode=feed&amp;tags=filetype:xml'>filetype:xml </a>
<a href='?tags=software:libervia'>software:libervia </a> <a href='?mode=feed&amp;tags=software:libervia'>software:libervia </a>
<a href='?tags=software:movim'>software:movim </a> <a href='?mode=feed&amp;tags=software:movim'>software:movim </a>
<a href='?tags=software:reeder'>software:reeder </a> <a href='?mode=feed&amp;tags=software:reeder'>software:reeder </a>
<a href='?tags=niche:syndication'>niche:syndication</a> <a href='?mode=feed&amp;tags=niche:syndication'>niche:syndication</a>
<h5 class='updated'>2024-08-22T17:09:04+03:00</h5> <h5 class='updated'>2024-08-22T17:09:04+03:00</h5>
<h5 class='author'> <h5 class='author'>
<a href='/jid/sch@pimux.de'>sch@pimux.de</a> <a href='/jid/sch@pimux.de'>sch@pimux.de</a>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>
@ -104,9 +107,10 @@
</p> </p>
<p> <p>
With the collaborative indexing system which Blasta offers, With the collaborative indexing system which Blasta offers,
you can be rest assured that you will find the references and its <a href="/help/about/folksonomy">folksonomical</a>
and resources that you need in order to be productive and nature you can be rest assured that you will find the
get that you need. references and resources that you need in order to be
productive and get that you need.
</p> </p>
<h4>The things that you can do with Blasta are endless</h4> <h4>The things that you can do with Blasta are endless</h4>
<p> <p>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -6,7 +6,7 @@
<link href="{{request.url}}" <link href="{{request.url}}"
rel="self" rel="self"
type="text/html" /> type="text/html" />
<link href="{{request.url.__str__().replace('?mode=feed', '')}}" <link href="{{request.url.__str__().replace('mode=feed&', '', 1)}}"
rel="alternate" rel="alternate"
type="text/html" /> type="text/html" />
<link href="xmpp:{{pubsub_jid}}?pubsub;action=subscribe;node={{node_id}}" <link href="xmpp:{{pubsub_jid}}?pubsub;action=subscribe;node={{node_id}}"

View file

@ -30,23 +30,46 @@
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd> <dd>
<a href="/jid">Public</a> <a href="/save">Add</a>
</dd> </dd>
<dd> <dd>
<a href="/private">Private</a> {% if node_type == 'public' %}
<b>Public</b>
{% else %}
<a href="/jid">Public</a>
{% endif %}
</dd> </dd>
<dd> <dd>
<a href="/read">Read</a> {% if node_type == 'private' %}
<b>Private</b>
{% else %}
<a href="/private">Private</a>
{% endif %}
</dd>
<dd>
{% if node_type == 'read' %}
<b>Read</b>
{% else %}
<a href="/read">Read</a>
{% endif %}
</dd> </dd>
{% endif %} {% endif %}
<dd> <dd>
<a href="/search{% if jid %}/jid/{{jid}}{% endif %}">Search</a> <a href="/search{% if jid %}/jid/{{jid}}{% endif %}">Search</a>
</dd> </dd>
<dd> <dd>
<a href="/popular">Popular</a> {% if page_type == 'popular' %}
<b>Popular</b>
{% else %}
<a href="/popular">Popular</a>
{% endif %}
</dd> </dd>
<dd> <dd>
<a href="/recent">Recent</a> {% if page_type == 'recent' %}
<b>Recent</b>
{% else %}
<a href="/recent">Recent</a>
{% endif %}
</dd> </dd>
<dd> <dd>
{% if jabber_id %} {% if jabber_id %}
@ -97,13 +120,13 @@
your PubSub bookmarks directory with Blasta! your PubSub bookmarks directory with Blasta!
</p> </p>
{% endif %} {% endif %}
<dl id="posts"> {% if entries %}
{% if entries %} <dl id="posts">
{% for entry in entries %} {% for entry in entries %}
<dd> <dd>
<article class="h-entry title"> <article class="h-entry">
<h4> <h4>
<a class="p-name" <a class="p-name title"
href="{{entry['link']}}"> href="{{entry['link']}}">
{{entry['title']}}</a> {{entry['title']}}</a>
</h4> </h4>
@ -123,7 +146,7 @@
delete</a> delete</a>
{% else %} {% else %}
<a href="/url/{{entry['url_hash']}}/edit"> <a href="/url/{{entry['url_hash']}}/edit">
add</a> add this</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
<p class="p-summary summary">{{entry['summary']}}</p> <p class="p-summary summary">{{entry['summary']}}</p>
@ -135,7 +158,6 @@
href="{% if jid %}/jid/{{jid}}{% elif path in ('query', 'url') %}/{% endif %}?tags={{tag}}"> href="{% if jid %}/jid/{{jid}}{% elif path in ('query', 'url') %}/{% endif %}?tags={{tag}}">
{{tag}} {{tag}}
</a> </a>
&#8203;&#8202;
{% endfor %} {% endfor %}
{% endif %} {% endif %}
by by
@ -167,7 +189,6 @@
<span>details</span> <span>details</span>
{% endif %} {% endif %}
</a> </a>
&#8203;&#8202;
at at
<span class="dt-published date" <span class="dt-published date"
datetime="{{entry['published']}}"> datetime="{{entry['published']}}">
@ -177,18 +198,23 @@
</article> </article>
</dd> </dd>
{% endfor %} {% endfor %}
{% endif %} </dl>
</dl> {% endif %}
</div> </div>
<div id="related-tags"> <div id="related-tags">
<h3>Related tags</h3> <h3>
{% if path == 'url' %}
Tags
{% else %}
Related tags
{% endif %}
</h3>
<dl> <dl>
{% for tag in tags %} {% for tag in tags %}
<dd> <dd>
<a href="{% if jid %}/jid/{{jid}}{% elif path in ('query', 'url') %}/{% endif %}?tags={{tag}}"> <a href="{% if jid %}/jid/{{jid}}{% elif path in ('query', 'url') %}/{% endif %}?tags={{tag}}">
{{tag}} {{tag}}
</a> </a>
&#8203;&#8202;
{% if tags[tag] %} {% if tags[tag] %}
({{tags[tag]}}) ({{tags[tag]}})
{% endif %} {% endif %}
@ -224,14 +250,12 @@
<a href="xmpp:{{pubsub_jid}}?pubsub;action=subscribe;node={{node_id}}"> <a href="xmpp:{{pubsub_jid}}?pubsub;action=subscribe;node={{node_id}}">
PubSub PubSub
</a> </a>
&#8203;&#8202;
and and
<img alt="⚛" class="enlarge" src="/graphic/syndicate.svg" width="16" height="16"/> <img alt="⚛" class="enlarge" src="/graphic/syndicate.svg" width="16" height="16"/>
<a href="/{% if jid %}jid/{% endif %}{{syndicate}}?mode=feed{% if param_tags %}&amp;tags={{param_tags}}{% endif %}{% if param_url %}&amp;url={{param_url}}{% endif %}{% if param_hash %}&amp;hash={{param_hash}}{% endif %}"> <a href="/{% if jid %}jid/{% endif %}{{syndicate}}?mode=feed{% if param_tags %}&amp;tags={{param_tags}}{% endif %}{% if param_url %}&amp;url={{param_url}}{% endif %}{% if param_hash %}&amp;hash={{param_hash}}{% endif %}">
RSS RSS
<!-- img src="/graphic/atom.svg" width="36" height="14" alt="Atom"/ --> <!-- img src="/graphic/atom.svg" width="36" height="14" alt="Atom"/ -->
</a> </a>
&#8203;&#8202;
syndication feeds for this page are available. syndication feeds for this page are available.
</p> </p>
</div> </div>

View file

@ -31,7 +31,7 @@
<a href="/recent">Recent</a> <a href="/recent">Recent</a>
</dd> </dd>
<dd> <dd>
<a href="/connect">Connect</a> <b>Connect</b>
</dd> </dd>
</dl> </dl>
</div> </div>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<b>Add</b>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>
@ -201,6 +204,17 @@ to view details.">
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td> <td>
{% if message %}
<p>
<i>
{{message}}
{% if message_link %}
<a href="{{message_link['href']}}">
{{message_link['text']}}</a>.
{% endif %}
</i>
</p>
{% endif %}
<p> <p>
<i> <i>
Fields that are marked with an asterisk Fields that are marked with an asterisk

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

209
template/folksonomy.xhtml Normal file
View file

@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="color-scheme" content="dark light" />
<title>Blasta / questions</title>
<link rel="shortcut icon" href="/graphic/blasta.svg"/>
<link rel='icon' type='image/svg+xml' href='/graphic/blasta.svg'/>
<link rel='stylesheet' type='text/css' media='screen' href='/stylesheet/stylesheet.css'/>
</head>
<body>
<div id="container">
<div id="header" class="row">
<h1>
<img src="/graphic/blasta.svg"/>
<a href="/">Blasta</a> / <a href="/help">help</a> / <a href="/help/about">about</a> / folksonomy
</h1>
<dl id="navigation">
<dd>
<img src="/graphic/blasta.svg"/>
</dd>
{% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd>
<a href="/jid">Public</a>
</dd>
<dd>
<a href="/private">Private</a>
</dd>
<dd>
<a href="/read">Read</a>
</dd>
{% endif %}
<dd>
<a href="/search{% if jabber_id %}/jid/{{jabber_id}}{% endif %}">Search</a>
</dd>
<dd>
<a href="/popular">Popular</a>
</dd>
<dd>
<a href="/recent">Recent</a>
</dd>
<dd>
{% if jabber_id %}
<a href="/disconnect">Disconnect</a>
{% else %}
<a href="/connect">Connect</a>
{% endif %}
</dd>
</dl>
</div>
<div id="main" class="row">
<div id="content">
<h2>&nbsp; PubSub Bookmarks</h2>
<p>
» A practice for people to organize and share information in
a collaborative and decentralized manner.
</p>
<h3>
Folksonomy
</h3>
<p>
Folksonomy is a system of classification that is created by
individual people, rather than by professionals (who
sometimes refer themselves as experts) such as archivers or
librarians. It is a fashion for people to collaboratively
tag and categorize information, often using keywords or
labels.
</p>
<h4>
Characteristics
</h4>
<p>
Among the characteristics of folksonomy are: independency,
collaborativeness, and being dynamic;
</p>
<p>
Tags are self-generated by individual people, not by a
centralized authority;
</p>
<p>
Tags can be added by multiple people to the same item,
and thereby they create a collective understanding;
</p>
<p>
Tags can be added, removed, and updated over time,
reflecting evolving usage, accuracy and interests;
</p>
<p>
There is no single source of truth or control over the
tagging process.
</p>
<h4>
Benefits
</h4>
<p>
Because people can easily find relevant content by searching
for specific tags; and shared tagging promotes collective
understanding and knowledge creation; and people feel more
involved and empowered by contributing their own insights.
</p>
<p>
The benefits of folksonomies spans from improved
discoverability of contents to enhanced collaboration and
increased engagement of people by contributing and
benefiting to the overhaul system.
</p>
<p>
It was written in Adam Mathes' recent paper on folksonomies that:
</p>
<p class="quote">
“There is a fundamental difference in the activities of
browsing to find interesting content, as opposed to direct
searching to find relevant documents in a query. It is
similar to the difference between exploring a problem space
to formulate questions, as opposed to actually looking for
answers to specifically formulated questions. Information
seeking behavior varies based on context.”
</p>
<h4>
Conclusion
</h4>
<p>
By practicing and engaging in folksonomy systems, people
find many interesting resources that are related to their
special and specific needs, that they could not have found
using search engines. Blasta has some interesting advantages
over search engines, and one of them is being folksonomy
driven.
</p>
<p>
The power of the people cannot be more evident than the
awesome resource that is Wikipedia, albeit is has been
gradually controlled by
<a href="https://www.fivefilters.org/2023/glenn-greenwald-on-wikipedia-bias/">
special interests</a>. Imagine what might happen if a search
engine (e.g. YaCy) can be engineered by people!
</p>
<h4>
Resources
</h4>
<p>
<ul>
<li>
<a href="http://adammathes.com/academic/computer-mediated-communication/folksonomies.html">
Folksonomies - Cooperative Classification and Communication Through Shared Metadata
</a>
&#8203;&#8202;
(adammathes.com)
</li>
<li>
<a href="http://www.sylloge.com/personal/2004/08/folksonomy-social-classification-great.html">
Folksonomy : social classification
</a>
&#8203;&#8202;
(sylloge.com)
</li>
</ul>
</p>
<br/>
<p class="quote bottom">
“I think the lack of hierarchy, synonym control and semantic
precision are precisely why it works. Free typing loose
associations is just a lot easier than making a decision
about the degree of match to a pre-defined category
(especially hierarchical ones).”
― Stewart Butterfield
</p>
</div>
</div>
<div id="footer" class="row">
<dl>
<dd>
<img src="/graphic/blasta.svg" alt="logo"/>
<a href="/">blasta</a>
</dd>
<dd>
<a href="/help/about">about</a>
</dd>
<dd>
<a href="/help/about/xmpp">xmpp</a>
</dd>
<dd>
<a href="/help/about/xmpp/pubsub">pubsub</a>
</dd>
<dd>
<a href="{{journal}}">journal</a>
</dd>
<dd>
<a href="/help">help</a>
</dd>
<dd>
<a href="/help/policy">policy</a>
</dd>
<dd>
<a href="/help/feeds">rss</a>
</dd>
<dd>
<a href="/contact">contact</a>
</dd>
</dl>
</div>
</div>
</body>
</html>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>
@ -64,6 +67,7 @@
<div> <div>
<h4>About</h4> <h4>About</h4>
<p><a href="/help/about">Blasta</a></p> <p><a href="/help/about">Blasta</a></p>
<p><a href="/help/about/folksonomy">Folksonomy</a></p>
<p><a href="/help/about/ideas">Ideas</a></p> <p><a href="/help/about/ideas">Ideas</a></p>
<p><a href="/help/about/philosophy">Philosophy</a></p> <p><a href="/help/about/philosophy">Philosophy</a></p>
<p><a href="/help/about/projects">Projects</a></p> <p><a href="/help/about/projects">Projects</a></p>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

256
template/people.xhtml Normal file
View file

@ -0,0 +1,256 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="color-scheme" content="dark light" />
<title>Blasta / {{path}}</title>
<link rel="shortcut icon" href="/graphic/blasta.svg" />
<link rel="icon" type="image/svg+xml" href="/graphic/blasta.svg" />
<link rel="stylesheet" type="text/css" media="screen"
href="/stylesheet/stylesheet.css" />
<link rel="alternate" type="application/atom+xml"
title="Follow updates on /{% if jid %}jid/{% endif %}{{syndicate}}{% if param_tags %} for Tag: #{{param_tags}}{% endif %}{% if param_url %} for URL: {{param_url}}{% endif %}{% if param_hash %} for hash: {{param_hash}}{% endif %}"
href="/{% if jid %}jid/{% endif %}{{syndicate}}?mode=feed{% if param_tags %}&amp;tags={{param_tags}}{% endif %}{% if param_url %}&amp;url={{param_url}}{% endif %}{% if param_hash %}&amp;hash={{param_hash}}{% endif %}" />
<link rel="alternate" type="application/atom+xml"
title="Subscribe to PubSub /{{syndicate}}{% if param_tags %} for Tag: #{{param_tags}}{% endif %}{% if param_url %} for URL: {{param_url}}{% endif %}{% if param_hash %} for hash: {{param_hash}}{% endif %}"
href="xmpp:{{pubsub_jid}}?pubsub;action=subscribe;node={{node_id}}" />
</head>
<body>
<div id="container">
<div id="header" class="row">
<h1>
<img src="/graphic/blasta.svg"/>
<a href="/">Blasta</a> / {{path}}
</h1>
<dl id="navigation">
<dd>
<img src="/graphic/blasta.svg"/>
</dd>
{% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd>
<a href="/jid">Public</a>
</dd>
<dd>
<a href="/private">Private</a>
</dd>
<dd>
<a href="/read">Read</a>
</dd>
{% endif %}
<dd>
<a href="/search{% if jid %}/jid/{{jid}}{% endif %}">Search</a>
</dd>
<dd>
<a href="/popular">Popular</a>
</dd>
<dd>
<a href="/recent">Recent</a>
</dd>
<dd>
{% if jabber_id %}
<a href="/disconnect">Disconnect</a>
{% else %}
<a href="/connect">Connect</a>
{% endif %}
</dd>
</dl>
</div>
<div id="main" class="row">
<div id="content">
<h2>&nbsp; PubSub Bookmarks</h2>
{% if pager %}
{% if page_next or page_prev %}
<div id="navigate-top">
{% if page_prev %}
«
<a href="?page={{page_prev}}{% if param_tags %}&amp;tags={{param_tags}}{% endif %}{% if param_query %}&amp;q={{param_query}}{% endif %}">
<b>retract</b></a>
{% else %}
<span class="inactive">« retract</span>
{% endif %}
or
{% if page_next %}
<a href="?page={{page_next}}{% if param_tags %}&amp;tags={{param_tags}}{% endif %}{% if param_query %}&amp;q={{param_query}}{% endif %}">
<b>proceed</b></a>
»
{% else %}
<span class="inactive">proceed »</span>
{% endif %}
</div>
{% endif %}
{% endif %}
<p>» {{message}}</p>
<h3>{{description}}</h3>
{% if entries %}
<dl id="posts">
{% for entry in entries %}
<dd>
<article>
<h4>
<a class="title"
href="{{entry['link']}}">
{{entry['title']}}</a>
</h4>
<!-- TODO Edit to be possible if entry is present -->
<!-- {% if jabber_id and entry['present'] %}{% endif %} -->
{% if jabber_id %}
{% if restore %}
<a href="{{link_save}}">restore</a>
{% elif delete %}
<a href="/url/{{entry['url_hash']}}/delete"
id="confirm">confirm deletion</a>
{% elif jabber_id == jid or exist or path in ('private', 'read') %}
<a href="/url/{{entry['url_hash']}}/edit">
edit</a>
/
<a href="/url/{{entry['url_hash']}}/confirm">
delete</a>
{% else %}
<a href="/url/{{entry['url_hash']}}/edit">
add</a>
{% endif %}
{% endif %}
<p class="summary">{{entry['summary']}}</p>
<div class="details">
{% if entry['tags'] | length > 0 %}
to
{% for tag in entry['tags'] %}
<a href="{% if jid %}/jid/{{jid}}{% elif path in ('query', 'url') %}/{% endif %}?tags={{tag}}">
{{tag}}
</a>
{% endfor %}
{% endif %}
at
<span>
{{entry['published_mod']}}
</span>
</div>
</article>
</dd>
{% endfor %}
</dl>
{% endif %}
<br/>
<hr/>
{% if people %}
<h4>
This resource is shared amongst {{entries[0]['instances']}} people
</h4>
<p>
<dl id="people">
{% for jid in people %}
<dt class="h-entry">
<dt>
<h5>
by <a class="p-author" href="/jid/{{jid}}">{{jid}}</a>
</h5>
</dt>
<dd>
to
{% if people[jid] | length > 0 %}
{% for tag in people[jid] %}
<a class="p-category"
href="{% if jid %}/jid/{{jid}}{% elif path in ('query', 'url') %}/{% endif %}?tags={{tag}}">
{{tag}}
</a>
{% endfor %}
{% endif %}
</dd>
</dt>
{% endfor %}
</dl>
</p>
{% endif %}
</div>
<div id="related-tags">
<h3>Your tags</h3>
<dl>
{% for tag in tags %}
<dd>
<a href="{% if jid %}/jid/{{jid}}{% elif path in ('query', 'url') %}/{% endif %}?tags={{tag}}">
{{tag}}
</a>
{% if tags[tag] %}
({{tags[tag]}})
{% endif %}
</dd>
{% endfor %}
<dd>
»
<a href="/tag/{% if jid %}{{jid}}{% endif %}">see more tags</a>
</dd>
</dl>
</div>
</div>
{% if pager %}
{% if page_next or page_prev %}
<div id="navigate-bottom" class="row">
{% if page_prev %}
« <a href="?page={{page_prev}}{% if param_tags %}&amp;tags={{param_tags}}{% endif %}{% if param_query %}&amp;q={{param_query}}{% endif %}"><b>retract</b></a>
{% else %}
<span class="inactive">« retract</span>
{% endif %}
or
{% if page_next %}
<a href="?page={{page_next}}{% if param_tags %}&amp;tags={{param_tags}}{% endif %}{% if param_query %}&amp;q={{param_query}}{% endif %}"><b>proceed</b></a> »
{% else %}
<span class="inactive">proceed »</span>
{% endif %}
</div>
{% endif %}
{% endif %}
<div id="subscribe" class="row">
<p>
<img alt="💡" src="/graphic/xmpp.svg" width="16" height="16"/>
<a href="xmpp:{{pubsub_jid}}?pubsub;action=subscribe;node={{node_id}}">
PubSub
</a>
and
<img alt="⚛" class="enlarge" src="/graphic/syndicate.svg" width="16" height="16"/>
<a href="/{% if jid %}jid/{% endif %}{{syndicate}}?mode=feed{% if param_tags %}&amp;tags={{param_tags}}{% endif %}{% if param_url %}&amp;url={{param_url}}{% endif %}{% if param_hash %}&amp;hash={{param_hash}}{% endif %}">
RSS
<!-- img src="/graphic/atom.svg" width="36" height="14" alt="Atom"/ -->
</a>
syndication feeds for this page are available.
</p>
</div>
<div id="footer" class="row">
<dl>
<dd>
<img src="/graphic/blasta.svg" alt="logo"/>
<a href="/">blasta</a>
</dd>
<dd>
<a href="/help/about">about</a>
</dd>
<dd>
<a href="/help/about/xmpp">xmpp</a>
</dd>
<dd>
<a href="/help/about/xmpp/pubsub">pubsub</a>
</dd>
<dd>
<a href="{{journal}}">journal</a>
</dd>
<dd>
<a href="/help">help</a>
</dd>
<dd>
<a href="/help/policy">policy</a>
</dd>
<dd>
<a href="/help/feeds">rss</a>
</dd>
<dd>
<a href="/contact">contact</a>
</dd>
</dl>
</div>
</div>
</body>
</html>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>
@ -79,7 +82,9 @@
<p> <p>
The method instance:keyword is a practice which improves The method instance:keyword is a practice which improves
bookmarks organizing, archiving, filtering and searching, as bookmarks organizing, archiving, filtering and searching, as
it narrows the context of a given bookmark. it narrows the context of a given bookmark, and therefore
improves <a href="/help/about/folksonomy">Folksonomy</a>
practices.
</p> </p>
<p> <p>
Suppose you have two bookmarks; one is related to Suppose you have two bookmarks; one is related to

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>
@ -33,7 +36,7 @@
</dd> </dd>
{% endif %} {% endif %}
<dd> <dd>
<a href="/search{% if jid %}/jid/{{jid}}{% endif %}">Search</a> <b>Search</b>
</dd> </dd>
<dd> <dd>
<a href="/popular">Popular</a> <a href="/popular">Popular</a>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>

View file

@ -22,6 +22,9 @@
<img src="/graphic/blasta.svg"/> <img src="/graphic/blasta.svg"/>
</dd> </dd>
{% if jabber_id %} {% if jabber_id %}
<dd>
<a href="/save">Add</a>
</dd>
<dd> <dd>
<a href="/jid">Public</a> <a href="/jid">Public</a>
</dd> </dd>