Offers collection for shared items

main
Bob Mottram 2023-06-27 17:41:33 +01:00
parent ed804adf85
commit 81f3b1d6a4
5 changed files with 220 additions and 1 deletions

View File

@ -185,6 +185,7 @@ from webapp_podcast import html_podcast_episode
from webapp_theme_designer import html_theme_designer from webapp_theme_designer import html_theme_designer
from webapp_minimalbutton import set_minimal from webapp_minimalbutton import set_minimal
from webapp_minimalbutton import is_minimal from webapp_minimalbutton import is_minimal
from webapp_utils import get_shares_collection
from webapp_utils import load_buy_sites from webapp_utils import load_buy_sites
from webapp_utils import get_default_path from webapp_utils import get_default_path
from webapp_utils import get_avatar_image_url from webapp_utils import get_avatar_image_url
@ -17230,6 +17231,88 @@ class PubServer(BaseHTTPRequestHandler):
self.server.debug) self.server.debug)
return return
# shared items offers collection for this instance
# this is only accessible to instance members or to
# other instances which present an authorization token
if self.path.startswith('/users/') and '/offers' in self.path:
offers_collection_authorized = authorized
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
page_number = 1
if '?page=' in self.path:
page_number_str = self.path.split('?page=')[1]
if ';' in page_number_str:
page_number_str = page_number_str.split(';')[0]
if page_number_str.isdigit():
page_number = int(page_number_str)
if not offers_collection_authorized:
if self.server.debug:
print('Offers collection access is not authorized. ' +
'Checking Authorization header')
# Check the authorization token
if self.headers.get('Origin') and \
self.headers.get('Authorization'):
permitted_domains = \
self.server.shared_items_federated_domains
shared_item_tokens = \
self.server.shared_item_federation_tokens
if authorize_shared_items(permitted_domains,
self.server.base_dir,
self.headers['Origin'],
calling_domain,
self.headers['Authorization'],
self.server.debug,
shared_item_tokens):
offers_collection_authorized = True
elif self.server.debug:
print('Authorization token refused for ' +
'offers collection federation')
# show offers collection for federation
if self._has_accept(calling_domain) and \
offers_collection_authorized:
if self.server.debug:
print('Preparing offers collection')
domain_full = self.server.domain_full
http_prefix = self.server.http_prefix
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if self.server.debug:
print('Offers collection for account: ' + nickname)
base_dir = self.server.base_dir
offers_items_per_page = 12
max_shares_per_account = offers_items_per_page
shared_items_federated_domains = \
self.server.shared_items_federated_domains
actor = \
local_actor_url(http_prefix, nickname, domain_full) + \
'/offers'
offers_json = \
get_shares_collection(actor, page_number,
offers_items_per_page, base_dir,
self.server.domain, nickname,
max_shares_per_account,
shared_items_federated_domains,
'shares')
msg_str = json.dumps(offers_json,
ensure_ascii=False)
msg_str = self._convert_domains(calling_domain,
referer_domain,
msg_str)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
self._set_headers(protocol_str, msglen,
None, calling_domain, False)
self._write(msg)
return
self._400()
return
# shared items catalog for this instance # shared items catalog for this instance
# this is only accessible to instance members or to # this is only accessible to instance members or to
# other instances which present an authorization token # other instances which present an authorization token

View File

@ -1032,6 +1032,41 @@ def get_shared_items_catalog_via_server(session, nickname: str, password: str,
return catalog_json return catalog_json
def get_offers_via_server(session, nickname: str, password: str,
domain: str, port: int,
http_prefix: str, debug: bool,
signing_priv_key_pem: str) -> {}:
"""Returns the offers collection for shared items via c2s
"""
if not session:
print('WARN: No session for get_offers_via_server')
return 6
auth_header = create_basic_auth_header(nickname, password)
headers = {
'host': domain,
'Content-type': 'application/json',
'Authorization': auth_header,
'Accept': 'application/json'
}
domain_full = get_full_domain(domain, port)
url = local_actor_url(http_prefix, nickname, domain_full) + '/offers'
if debug:
print('Offers collection request to: ' + url)
offers_json = get_json(signing_priv_key_pem, session, url, headers, None,
debug, __version__, http_prefix, None)
if not offers_json:
if debug:
print('DEBUG: GET offers collection failed for c2s to ' + url)
# return 5
if debug:
print('DEBUG: c2s GET offers collection success')
return offers_json
def outbox_share_upload(base_dir: str, http_prefix: str, def outbox_share_upload(base_dir: str, http_prefix: str,
nickname: str, domain: str, port: int, nickname: str, domain: str, port: int,
message_json: {}, debug: bool, city: str, message_json: {}, debug: bool, city: str,

View File

@ -198,6 +198,7 @@ from shares import update_shared_item_federation_token
from shares import merge_shared_item_tokens from shares import merge_shared_item_tokens
from shares import send_share_via_server from shares import send_share_via_server
from shares import get_shared_items_catalog_via_server from shares import get_shared_items_catalog_via_server
from shares import get_offers_via_server
from cwlists import add_cw_from_lists from cwlists import add_cw_from_lists
from cwlists import load_cw_lists from cwlists import load_cw_lists
from happening import dav_month_via_server from happening import dav_month_via_server
@ -2080,6 +2081,17 @@ def test_shared_items_federation(base_dir: str) -> None:
assert 'DFC:supplies' in catalog_json assert 'DFC:supplies' in catalog_json
assert len(catalog_json.get('DFC:supplies')) == 3 assert len(catalog_json.get('DFC:supplies')) == 3
offers_json = \
get_offers_via_server(session_bob, 'bob', bob_password,
bob_domain, bob_port,
http_prefix, True,
signing_priv_key_pem)
assert offers_json
print('Offers collection:')
pprint(offers_json)
assert isinstance(offers_json, list)
assert len(offers_json) == 1
print('\n\n*********************************************************') print('\n\n*********************************************************')
print('Alice sends a message to Bob') print('Alice sends a message to Bob')
alice_tokens_filename = \ alice_tokens_filename = \

View File

@ -2398,7 +2398,7 @@ def _get_reserved_words() -> str:
'mention', 'http', 'https', 'ipfs', 'ipns', 'mention', 'http', 'https', 'ipfs', 'ipns',
'ontologies', 'data', 'postedit', 'moved', 'ontologies', 'data', 'postedit', 'moved',
'inactive', 'activitypub', 'actors', 'inactive', 'activitypub', 'actors',
'notes') 'notes', 'offers', 'wanted')
def get_nickname_validation_pattern() -> str: def get_nickname_validation_pattern() -> str:

View File

@ -11,6 +11,7 @@ import os
from shutil import copyfile from shutil import copyfile
from collections import OrderedDict from collections import OrderedDict
from session import get_json from session import get_json
from utils import get_media_extensions
from utils import dangerous_markup from utils import dangerous_markup
from utils import acct_handle_dir from utils import acct_handle_dir
from utils import remove_id_ending from utils import remove_id_ending
@ -556,6 +557,94 @@ def shares_timeline_json(actor: str, page_number: int, items_per_page: int,
return result_json, last_page return result_json, last_page
def get_shares_collection(actor: str, page_number: int, items_per_page: int,
base_dir: str, domain: str, nickname: str,
max_shares_per_account: int,
shared_items_federated_domains: [],
shares_file_type: str) -> {}:
"""Returns an ActivityStreams collection of Offer activities
https://www.w3.org/TR/activitystreams-vocabulary/#dfn-offer
"""
shares_collection = []
shares_json, _ = \
shares_timeline_json(actor, page_number, items_per_page,
base_dir, domain, nickname,
max_shares_per_account,
shared_items_federated_domains, shares_file_type)
if shares_file_type == 'shares':
share_type = 'Offer'
else:
share_type = 'Want'
for _, shared_item in shares_json.items():
if not shared_item.get('shareId'):
continue
if not shared_item.get('itemType'):
continue
share_id = shared_item['shareId'].replace('___', '://')
share_id = share_id.replace('--', '/')
offer_item = {
"@context": "https://www.w3.org/ns/activitystreams",
"summary": shared_item['summary'],
"type": share_type,
"actor": shared_item['actor'],
"id": share_id,
"published": shared_item['published'],
"object": {
"id": share_id,
"type": shared_item['itemType'].title(),
"name": shared_item['displayName'],
"published": shared_item['published'],
"attachment": []
}
}
if shared_item['category']:
offer_item['object']['attachment'].append({
"type": "PropertyValue",
"name": "category",
"value": shared_item['category']
})
if shared_item['location']:
offer_item['object']['attachment'].append({
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Place",
"name": shared_item['location'].title()
})
if shared_item['imageUrl']:
if '://' in shared_item['imageUrl']:
file_extension = None
accepted_types = get_media_extensions()
for mtype in accepted_types:
if shared_item['imageUrl'].endswith('.' + mtype):
if mtype == 'jpg':
mtype = 'jpeg'
if mtype == 'mp3':
mtype = 'mpeg'
file_extension = mtype
if file_extension:
media_type = 'image/' + file_extension
offer_item['object']['attachment'].append({
'mediaType': media_type,
'name': shared_item['displayName'],
'type': 'Document',
'url': shared_item['imageUrl']
})
if shared_item['itemPrice'] and shared_item['itemCurrency']:
offer_item['object']['attachment'].append({
"type": "PropertyValue",
"name": "price",
"value": shared_item['itemPrice']
})
offer_item['object']['attachment'].append({
"type": "PropertyValue",
"name": "currency",
"value": shared_item['itemCurrency']
})
shares_collection.append(offer_item)
return shares_collection
def post_contains_public(post_json_object: {}) -> bool: def post_contains_public(post_json_object: {}) -> bool:
"""Does the given post contain #Public """Does the given post contain #Public
""" """