Proxy settings
|
|
@ -3,6 +3,6 @@ image: debian:testing
|
|||
test:
|
||||
script:
|
||||
- apt-get update
|
||||
- apt-get install -y python3-cryptography python3-dateutil python3-idna python3-numpy python3-pil.imagetk python3-requests python3-socks python3-setuptools python3-pyqrcode
|
||||
- apt-get install -y python3-cryptography python3-dateutil python3-idna python3-numpy python3-pil.imagetk python3-requests python3-socks python3-setuptools python3-pyqrcode imagemagick gnupg
|
||||
- python3 epicyon.py --tests
|
||||
- python3 epicyon.py --testsnetwork
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ By submitting code, documentation or artwork you agree that it will be licensed
|
|||
|
||||
## Security Vulnerability Disclosure
|
||||
|
||||
Create an issue on https://gitlab.com/bashrc2/epicyon/issues. If the vulnerability is especially sensitive then send an XMPP message to **bob@freedombone.net** or a Matrix message to **@bob:matrix.freedombone.net**.
|
||||
Create an issue on https://gitlab.com/bashrc2/epicyon/issues. If the vulnerability is especially sensitive then send an XMPP message to **bob@libreserver.org** or a Matrix message to **@bob:matrix.libreserver.org**.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ The code of conduct can be found [here](code-of-conduct.md).
|
|||
|
||||
Submit to https://gitlab.com/bashrc2/epicyon/issues
|
||||
|
||||
You can also post patches in the old-fashioned style via email to **bob@freedombone.net**. Include **[Epicyon]** in the subject line, otherwise it may be ignored.
|
||||
You can also post patches in the old-fashioned style via email to **bob@libreserver.org**. Include **[Epicyon]** in the subject line, otherwise it may be ignored.
|
||||
|
||||
## Development Style
|
||||
|
||||
|
|
|
|||
|
|
@ -165,6 +165,9 @@ server {
|
|||
try_files $uri =404;
|
||||
}
|
||||
|
||||
keepalive_timeout 70;
|
||||
sendfile on;
|
||||
|
||||
location / {
|
||||
proxy_http_version 1.1;
|
||||
client_max_body_size 31M;
|
||||
|
|
@ -184,6 +187,7 @@ server {
|
|||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
proxy_pass http://localhost:7156;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
import os
|
||||
from utils import hasObjectStringObject
|
||||
from utils import hasUsersPath
|
||||
from utils import getFullDomain
|
||||
from utils import urlPermitted
|
||||
|
|
@ -15,10 +16,11 @@ from utils import getDomainFromActor
|
|||
from utils import getNicknameFromActor
|
||||
from utils import domainPermitted
|
||||
from utils import followPerson
|
||||
from utils import hasObjectDict
|
||||
from utils import acctDir
|
||||
from utils import hasGroupType
|
||||
from utils import localActorUrl
|
||||
from utils import hasActor
|
||||
from utils import hasObjectStringType
|
||||
|
||||
|
||||
def _createAcceptReject(baseDir: str, federationList: [],
|
||||
|
|
@ -77,9 +79,7 @@ def _acceptFollow(baseDir: str, domain: str, messageJson: {},
|
|||
federationList: [], debug: bool) -> None:
|
||||
"""Receiving a follow Accept activity
|
||||
"""
|
||||
if not hasObjectDict(messageJson):
|
||||
return
|
||||
if not messageJson['object'].get('type'):
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if not messageJson['object']['type'] == 'Follow':
|
||||
if not messageJson['object']['type'] == 'Join':
|
||||
|
|
@ -90,8 +90,7 @@ def _acceptFollow(baseDir: str, domain: str, messageJson: {},
|
|||
print('DEBUG: no actor in Follow activity')
|
||||
return
|
||||
# no, this isn't a mistake
|
||||
if not messageJson['object'].get('object'):
|
||||
print('DEBUG: no object within Follow activity')
|
||||
if not hasObjectStringObject(messageJson, debug):
|
||||
return
|
||||
if not messageJson.get('to'):
|
||||
if debug:
|
||||
|
|
@ -191,9 +190,7 @@ def receiveAcceptReject(session, baseDir: str,
|
|||
"""
|
||||
if messageJson['type'] != 'Accept' and messageJson['type'] != 'Reject':
|
||||
return False
|
||||
if not messageJson.get('actor'):
|
||||
if debug:
|
||||
print('DEBUG: ' + messageJson['type'] + ' has no actor')
|
||||
if not hasActor(messageJson, debug):
|
||||
return False
|
||||
if not hasUsersPath(messageJson['actor']):
|
||||
if debug:
|
||||
|
|
|
|||
81
announce.py
|
|
@ -3,13 +3,13 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
from utils import hasObjectStringObject
|
||||
from utils import hasGroupType
|
||||
from utils import removeDomainPort
|
||||
from utils import hasObjectDict
|
||||
from utils import removeIdEnding
|
||||
from utils import hasUsersPath
|
||||
from utils import getFullDomain
|
||||
|
|
@ -23,6 +23,9 @@ from utils import saveJson
|
|||
from utils import undoAnnounceCollectionEntry
|
||||
from utils import updateAnnounceCollection
|
||||
from utils import localActorUrl
|
||||
from utils import replaceUsersWithAt
|
||||
from utils import hasActor
|
||||
from utils import hasObjectStringType
|
||||
from posts import sendSignedJson
|
||||
from posts import getPersonBox
|
||||
from session import postJson
|
||||
|
|
@ -53,7 +56,7 @@ def outboxAnnounce(recentPostsCache: {},
|
|||
""" Adds or removes announce entries from the shares collection
|
||||
within a given post
|
||||
"""
|
||||
if not messageJson.get('actor'):
|
||||
if not hasActor(messageJson, debug):
|
||||
return False
|
||||
if not isinstance(messageJson['actor'], str):
|
||||
return False
|
||||
|
|
@ -79,9 +82,7 @@ def outboxAnnounce(recentPostsCache: {},
|
|||
nickname, domain, debug)
|
||||
return True
|
||||
elif messageJson['type'] == 'Undo':
|
||||
if not hasObjectDict(messageJson):
|
||||
return False
|
||||
if not messageJson['object'].get('type'):
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return False
|
||||
if messageJson['object']['type'] == 'Announce':
|
||||
if not isinstance(messageJson['object']['object'], str):
|
||||
|
|
@ -121,7 +122,8 @@ def createAnnounce(session, baseDir: str, federationList: [],
|
|||
clientToServer: bool,
|
||||
sendThreads: [], postLog: [],
|
||||
personCache: {}, cachedWebfingers: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Creates an announce message
|
||||
Typically toUrl will be https://www.w3.org/ns/activitystreams#Public
|
||||
and ccUrl might be a specific person favorited or repeated and the
|
||||
|
|
@ -178,7 +180,8 @@ def createAnnounce(session, baseDir: str, federationList: [],
|
|||
announceNickname, announceDomain, announcePort, None,
|
||||
httpPrefix, True, clientToServer, federationList,
|
||||
sendThreads, postLog, cachedWebfingers, personCache,
|
||||
debug, projectVersion, None, groupAccount)
|
||||
debug, projectVersion, None, groupAccount,
|
||||
signingPrivateKeyPem, 639633)
|
||||
|
||||
return newAnnounce
|
||||
|
||||
|
|
@ -188,7 +191,8 @@ def announcePublic(session, baseDir: str, federationList: [],
|
|||
objectUrl: str, clientToServer: bool,
|
||||
sendThreads: [], postLog: [],
|
||||
personCache: {}, cachedWebfingers: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Makes a public announcement
|
||||
"""
|
||||
fromDomain = getFullDomain(domain, port)
|
||||
|
|
@ -201,7 +205,8 @@ def announcePublic(session, baseDir: str, federationList: [],
|
|||
objectUrl, True, clientToServer,
|
||||
sendThreads, postLog,
|
||||
personCache, cachedWebfingers,
|
||||
debug, projectVersion)
|
||||
debug, projectVersion,
|
||||
signingPrivateKeyPem)
|
||||
|
||||
|
||||
def sendAnnounceViaServer(baseDir: str, session,
|
||||
|
|
@ -209,7 +214,8 @@ def sendAnnounceViaServer(baseDir: str, session,
|
|||
fromDomain: str, fromPort: int,
|
||||
httpPrefix: str, repeatObjectUrl: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Creates an announce message via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -241,7 +247,8 @@ def sendAnnounceViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = webfingerHandle(session, handle, httpPrefix,
|
||||
cachedWebfingers,
|
||||
fromDomain, projectVersion, debug, False)
|
||||
fromDomain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: announce webfinger failed for ' + handle)
|
||||
|
|
@ -254,13 +261,16 @@ def sendAnnounceViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
originDomain = fromDomain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId,
|
||||
sharedInbox, avatarUrl,
|
||||
displayName) = getPersonBox(baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
fromNickname, fromDomain,
|
||||
postToBox, 73528)
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem,
|
||||
originDomain,
|
||||
baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
fromNickname, fromDomain,
|
||||
postToBox, 73528)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -297,7 +307,8 @@ def sendUndoAnnounceViaServer(baseDir: str, session,
|
|||
domain: str, port: int,
|
||||
httpPrefix: str, repeatObjectUrl: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Undo an announce message via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -307,7 +318,7 @@ def sendUndoAnnounceViaServer(baseDir: str, session,
|
|||
domainFull = getFullDomain(domain, port)
|
||||
|
||||
actor = localActorUrl(httpPrefix, nickname, domainFull)
|
||||
handle = actor.replace('/users/', '/@')
|
||||
handle = replaceUsersWithAt(actor)
|
||||
|
||||
statusNumber, published = getStatusNumber()
|
||||
unAnnounceJson = {
|
||||
|
|
@ -321,7 +332,8 @@ def sendUndoAnnounceViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = webfingerHandle(session, handle, httpPrefix,
|
||||
cachedWebfingers,
|
||||
domain, projectVersion, debug, False)
|
||||
domain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: undo announce webfinger failed for ' + handle)
|
||||
|
|
@ -334,13 +346,16 @@ def sendUndoAnnounceViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
originDomain = domain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId,
|
||||
sharedInbox, avatarUrl,
|
||||
displayName) = getPersonBox(baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
nickname, domain,
|
||||
postToBox, 73528)
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem,
|
||||
originDomain,
|
||||
baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
nickname, domain,
|
||||
postToBox, 73528)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -381,25 +396,13 @@ def outboxUndoAnnounce(recentPostsCache: {},
|
|||
return
|
||||
if not messageJson['type'] == 'Undo':
|
||||
return
|
||||
if not hasObjectDict(messageJson):
|
||||
if debug:
|
||||
print('DEBUG: undo like object is not dict')
|
||||
return
|
||||
if not messageJson['object'].get('type'):
|
||||
if debug:
|
||||
print('DEBUG: undo like - no type')
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if not messageJson['object']['type'] == 'Announce':
|
||||
if debug:
|
||||
print('DEBUG: not a undo announce')
|
||||
return
|
||||
if not messageJson['object'].get('object'):
|
||||
if debug:
|
||||
print('DEBUG: no object in undo announce')
|
||||
return
|
||||
if not isinstance(messageJson['object']['object'], str):
|
||||
if debug:
|
||||
print('DEBUG: undo announce object is not string')
|
||||
if not hasObjectStringObject(messageJson, debug):
|
||||
return
|
||||
if debug:
|
||||
print('DEBUG: c2s undo announce request arrived in outbox')
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 166 KiB |
2
auth.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Security"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
|
|
@ -12,6 +12,7 @@ from webfinger import webfingerHandle
|
|||
from auth import createBasicAuthHeader
|
||||
from posts import getPersonBox
|
||||
from session import postJson
|
||||
from utils import hasObjectString
|
||||
from utils import getFullDomain
|
||||
from utils import getNicknameFromActor
|
||||
from utils import getDomainFromActor
|
||||
|
|
@ -19,6 +20,7 @@ from utils import loadJson
|
|||
from utils import saveJson
|
||||
from utils import acctDir
|
||||
from utils import localActorUrl
|
||||
from utils import hasActor
|
||||
|
||||
|
||||
def setAvailability(baseDir: str, nickname: str, domain: str,
|
||||
|
|
@ -60,11 +62,9 @@ def outboxAvailability(baseDir: str, nickname: str, messageJson: {},
|
|||
return False
|
||||
if not messageJson['type'] == 'Availability':
|
||||
return False
|
||||
if not messageJson.get('actor'):
|
||||
if not hasActor(messageJson, debug):
|
||||
return False
|
||||
if not messageJson.get('object'):
|
||||
return False
|
||||
if not isinstance(messageJson['object'], str):
|
||||
if not hasObjectString(messageJson, debug):
|
||||
return False
|
||||
|
||||
actorNickname = getNicknameFromActor(messageJson['actor'])
|
||||
|
|
@ -82,7 +82,8 @@ def sendAvailabilityViaServer(baseDir: str, session,
|
|||
httpPrefix: str,
|
||||
status: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Sets the availability for a person via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -107,7 +108,8 @@ def sendAvailabilityViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = webfingerHandle(session, handle, httpPrefix,
|
||||
cachedWebfingers,
|
||||
domain, projectVersion, debug, False)
|
||||
domain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: availability webfinger failed for ' + handle)
|
||||
|
|
@ -120,12 +122,14 @@ def sendAvailabilityViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
(inboxUrl, pubKeyId, pubKey,
|
||||
fromPersonId, sharedInbox,
|
||||
avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest,
|
||||
personCache, projectVersion,
|
||||
httpPrefix, nickname,
|
||||
domain, postToBox, 57262)
|
||||
originDomain = domain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem,
|
||||
originDomain,
|
||||
baseDir, session, wfRequest,
|
||||
personCache, projectVersion,
|
||||
httpPrefix, nickname,
|
||||
domain, postToBox, 57262)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
|
|||
299
blocking.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
|
||||
|
|
@ -11,6 +11,9 @@ import os
|
|||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
from utils import hasObjectString
|
||||
from utils import hasObjectStringObject
|
||||
from utils import hasObjectStringType
|
||||
from utils import removeDomainPort
|
||||
from utils import hasObjectDict
|
||||
from utils import isAccountDir
|
||||
|
|
@ -29,6 +32,7 @@ from utils import getDomainFromActor
|
|||
from utils import getNicknameFromActor
|
||||
from utils import acctDir
|
||||
from utils import localActorUrl
|
||||
from utils import hasActor
|
||||
from conversation import muteConversation
|
||||
from conversation import unmuteConversation
|
||||
|
||||
|
|
@ -346,13 +350,7 @@ def outboxBlock(baseDir: str, httpPrefix: str,
|
|||
if debug:
|
||||
print('DEBUG: not a block')
|
||||
return False
|
||||
if not messageJson.get('object'):
|
||||
if debug:
|
||||
print('DEBUG: no object in block')
|
||||
return False
|
||||
if not isinstance(messageJson['object'], str):
|
||||
if debug:
|
||||
print('DEBUG: block object is not string')
|
||||
if not hasObjectString(messageJson, debug):
|
||||
return False
|
||||
if debug:
|
||||
print('DEBUG: c2s block request arrived in outbox')
|
||||
|
|
@ -401,26 +399,14 @@ def outboxUndoBlock(baseDir: str, httpPrefix: str,
|
|||
if debug:
|
||||
print('DEBUG: not an undo block')
|
||||
return
|
||||
if not hasObjectDict(messageJson):
|
||||
if debug:
|
||||
print('DEBUG: undo block object is not string')
|
||||
return
|
||||
|
||||
if not messageJson['object'].get('type'):
|
||||
if debug:
|
||||
print('DEBUG: undo block - no type')
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if not messageJson['object']['type'] == 'Block':
|
||||
if debug:
|
||||
print('DEBUG: not an undo block')
|
||||
return
|
||||
if not messageJson['object'].get('object'):
|
||||
if debug:
|
||||
print('DEBUG: no object in undo block')
|
||||
return
|
||||
if not isinstance(messageJson['object']['object'], str):
|
||||
if debug:
|
||||
print('DEBUG: undo block object is not string')
|
||||
if not hasObjectStringObject(messageJson, debug):
|
||||
return
|
||||
if debug:
|
||||
print('DEBUG: c2s undo block request arrived in outbox')
|
||||
|
|
@ -461,52 +447,65 @@ def mutePost(baseDir: str, nickname: str, domain: str, port: int,
|
|||
debug: bool) -> None:
|
||||
""" Mutes the given post
|
||||
"""
|
||||
print('mutePost: postId ' + postId)
|
||||
postFilename = locatePost(baseDir, nickname, domain, postId)
|
||||
if not postFilename:
|
||||
print('mutePost: file not found ' + postId)
|
||||
return
|
||||
postJsonObject = loadJson(postFilename)
|
||||
if not postJsonObject:
|
||||
print('mutePost: object not loaded ' + postId)
|
||||
return
|
||||
print('mutePost: ' + str(postJsonObject))
|
||||
|
||||
postJsonObj = postJsonObject
|
||||
alsoUpdatePostId = None
|
||||
if hasObjectDict(postJsonObject):
|
||||
domainFull = getFullDomain(domain, port)
|
||||
actor = localActorUrl(httpPrefix, nickname, domainFull)
|
||||
postJsonObj = postJsonObject['object']
|
||||
else:
|
||||
if hasObjectString(postJsonObject, debug):
|
||||
alsoUpdatePostId = removeIdEnding(postJsonObject['object'])
|
||||
|
||||
if postJsonObject['object'].get('conversation'):
|
||||
muteConversation(baseDir, nickname, domain,
|
||||
postJsonObject['object']['conversation'])
|
||||
domainFull = getFullDomain(domain, port)
|
||||
actor = localActorUrl(httpPrefix, nickname, domainFull)
|
||||
|
||||
# does this post have ignores on it from differenent actors?
|
||||
if not postJsonObject['object'].get('ignores'):
|
||||
if debug:
|
||||
print('DEBUG: Adding initial mute to ' + postId)
|
||||
ignoresJson = {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
'id': postId,
|
||||
'type': 'Collection',
|
||||
"totalItems": 1,
|
||||
'items': [{
|
||||
'type': 'Ignore',
|
||||
'actor': actor
|
||||
}]
|
||||
}
|
||||
postJsonObject['object']['ignores'] = ignoresJson
|
||||
else:
|
||||
if not postJsonObject['object']['ignores'].get('items'):
|
||||
postJsonObject['object']['ignores']['items'] = []
|
||||
itemsList = postJsonObject['object']['ignores']['items']
|
||||
for ignoresItem in itemsList:
|
||||
if ignoresItem.get('actor'):
|
||||
if ignoresItem['actor'] == actor:
|
||||
return
|
||||
newIgnore = {
|
||||
if postJsonObj.get('conversation'):
|
||||
muteConversation(baseDir, nickname, domain,
|
||||
postJsonObj['conversation'])
|
||||
|
||||
# does this post have ignores on it from differenent actors?
|
||||
if not postJsonObj.get('ignores'):
|
||||
if debug:
|
||||
print('DEBUG: Adding initial mute to ' + postId)
|
||||
ignoresJson = {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
'id': postId,
|
||||
'type': 'Collection',
|
||||
"totalItems": 1,
|
||||
'items': [{
|
||||
'type': 'Ignore',
|
||||
'actor': actor
|
||||
}
|
||||
igIt = len(itemsList)
|
||||
itemsList.append(newIgnore)
|
||||
postJsonObject['object']['ignores']['totalItems'] = igIt
|
||||
saveJson(postJsonObject, postFilename)
|
||||
}]
|
||||
}
|
||||
postJsonObj['ignores'] = ignoresJson
|
||||
else:
|
||||
if not postJsonObj['ignores'].get('items'):
|
||||
postJsonObj['ignores']['items'] = []
|
||||
itemsList = postJsonObj['ignores']['items']
|
||||
for ignoresItem in itemsList:
|
||||
if ignoresItem.get('actor'):
|
||||
if ignoresItem['actor'] == actor:
|
||||
return
|
||||
newIgnore = {
|
||||
'type': 'Ignore',
|
||||
'actor': actor
|
||||
}
|
||||
igIt = len(itemsList)
|
||||
itemsList.append(newIgnore)
|
||||
postJsonObj['ignores']['totalItems'] = igIt
|
||||
postJsonObj['muted'] = True
|
||||
if saveJson(postJsonObject, postFilename):
|
||||
print('mutePost: saved ' + postFilename)
|
||||
|
||||
# remove cached post so that the muted version gets recreated
|
||||
# without its content text and/or image
|
||||
|
|
@ -514,7 +513,13 @@ def mutePost(baseDir: str, nickname: str, domain: str, port: int,
|
|||
getCachedPostFilename(baseDir, nickname, domain, postJsonObject)
|
||||
if cachedPostFilename:
|
||||
if os.path.isfile(cachedPostFilename):
|
||||
os.remove(cachedPostFilename)
|
||||
try:
|
||||
os.remove(cachedPostFilename)
|
||||
print('MUTE: cached post removed ' + cachedPostFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
else:
|
||||
print('MUTE: cached post not found ' + cachedPostFilename)
|
||||
|
||||
with open(postFilename + '.muted', 'w+') as muteFile:
|
||||
muteFile.write('\n')
|
||||
|
|
@ -526,14 +531,39 @@ def mutePost(baseDir: str, nickname: str, domain: str, port: int,
|
|||
removeIdEnding(postJsonObject['id']).replace('/', '#')
|
||||
if postId in recentPostsCache['index']:
|
||||
print('MUTE: ' + postId + ' is in recent posts cache')
|
||||
if recentPostsCache['json'].get(postId):
|
||||
postJsonObject['muted'] = True
|
||||
recentPostsCache['json'][postId] = json.dumps(postJsonObject)
|
||||
if recentPostsCache.get('html'):
|
||||
if recentPostsCache['html'].get(postId):
|
||||
del recentPostsCache['html'][postId]
|
||||
print('MUTE: ' + postId +
|
||||
' marked as muted in recent posts memory cache')
|
||||
if recentPostsCache.get('json'):
|
||||
recentPostsCache['json'][postId] = json.dumps(postJsonObject)
|
||||
print('MUTE: ' + postId +
|
||||
' marked as muted in recent posts memory cache')
|
||||
if recentPostsCache.get('html'):
|
||||
if recentPostsCache['html'].get(postId):
|
||||
del recentPostsCache['html'][postId]
|
||||
print('MUTE: ' + postId + ' removed cached html')
|
||||
|
||||
if alsoUpdatePostId:
|
||||
postFilename = locatePost(baseDir, nickname, domain, alsoUpdatePostId)
|
||||
if os.path.isfile(postFilename):
|
||||
postJsonObj = loadJson(postFilename)
|
||||
cachedPostFilename = \
|
||||
getCachedPostFilename(baseDir, nickname, domain,
|
||||
postJsonObj)
|
||||
if cachedPostFilename:
|
||||
if os.path.isfile(cachedPostFilename):
|
||||
try:
|
||||
os.remove(cachedPostFilename)
|
||||
print('MUTE: cached referenced post removed ' +
|
||||
cachedPostFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
if recentPostsCache.get('json'):
|
||||
if recentPostsCache['json'].get(alsoUpdatePostId):
|
||||
del recentPostsCache['json'][alsoUpdatePostId]
|
||||
print('MUTE: ' + alsoUpdatePostId + ' removed referenced json')
|
||||
if recentPostsCache.get('html'):
|
||||
if recentPostsCache['html'].get(alsoUpdatePostId):
|
||||
del recentPostsCache['html'][alsoUpdatePostId]
|
||||
print('MUTE: ' + alsoUpdatePostId + ' removed referenced html')
|
||||
|
||||
|
||||
def unmutePost(baseDir: str, nickname: str, domain: str, port: int,
|
||||
|
|
@ -550,37 +580,47 @@ def unmutePost(baseDir: str, nickname: str, domain: str, port: int,
|
|||
|
||||
muteFilename = postFilename + '.muted'
|
||||
if os.path.isfile(muteFilename):
|
||||
os.remove(muteFilename)
|
||||
try:
|
||||
os.remove(muteFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
print('UNMUTE: ' + muteFilename + ' file removed')
|
||||
|
||||
postJsonObj = postJsonObject
|
||||
alsoUpdatePostId = None
|
||||
if hasObjectDict(postJsonObject):
|
||||
if postJsonObject['object'].get('conversation'):
|
||||
unmuteConversation(baseDir, nickname, domain,
|
||||
postJsonObject['object']['conversation'])
|
||||
postJsonObj = postJsonObject['object']
|
||||
else:
|
||||
if hasObjectString(postJsonObject, debug):
|
||||
alsoUpdatePostId = removeIdEnding(postJsonObject['object'])
|
||||
|
||||
if postJsonObject['object'].get('ignores'):
|
||||
domainFull = getFullDomain(domain, port)
|
||||
actor = localActorUrl(httpPrefix, nickname, domainFull)
|
||||
totalItems = 0
|
||||
if postJsonObject['object']['ignores'].get('totalItems'):
|
||||
totalItems = \
|
||||
postJsonObject['object']['ignores']['totalItems']
|
||||
itemsList = postJsonObject['object']['ignores']['items']
|
||||
for ignoresItem in itemsList:
|
||||
if ignoresItem.get('actor'):
|
||||
if ignoresItem['actor'] == actor:
|
||||
if debug:
|
||||
print('DEBUG: mute was removed for ' + actor)
|
||||
itemsList.remove(ignoresItem)
|
||||
break
|
||||
if totalItems == 1:
|
||||
if debug:
|
||||
print('DEBUG: mute was removed from post')
|
||||
del postJsonObject['object']['ignores']
|
||||
else:
|
||||
igItLen = len(postJsonObject['object']['ignores']['items'])
|
||||
postJsonObject['object']['ignores']['totalItems'] = igItLen
|
||||
saveJson(postJsonObject, postFilename)
|
||||
if postJsonObj.get('conversation'):
|
||||
unmuteConversation(baseDir, nickname, domain,
|
||||
postJsonObj['conversation'])
|
||||
|
||||
if postJsonObj.get('ignores'):
|
||||
domainFull = getFullDomain(domain, port)
|
||||
actor = localActorUrl(httpPrefix, nickname, domainFull)
|
||||
totalItems = 0
|
||||
if postJsonObj['ignores'].get('totalItems'):
|
||||
totalItems = postJsonObj['ignores']['totalItems']
|
||||
itemsList = postJsonObj['ignores']['items']
|
||||
for ignoresItem in itemsList:
|
||||
if ignoresItem.get('actor'):
|
||||
if ignoresItem['actor'] == actor:
|
||||
if debug:
|
||||
print('DEBUG: mute was removed for ' + actor)
|
||||
itemsList.remove(ignoresItem)
|
||||
break
|
||||
if totalItems == 1:
|
||||
if debug:
|
||||
print('DEBUG: mute was removed from post')
|
||||
del postJsonObj['ignores']
|
||||
else:
|
||||
igItLen = len(postJsonObj['ignores']['items'])
|
||||
postJsonObj['ignores']['totalItems'] = igItLen
|
||||
postJsonObj['muted'] = False
|
||||
saveJson(postJsonObject, postFilename)
|
||||
|
||||
# remove cached post so that the muted version gets recreated
|
||||
# with its content text and/or image
|
||||
|
|
@ -588,7 +628,10 @@ def unmutePost(baseDir: str, nickname: str, domain: str, port: int,
|
|||
getCachedPostFilename(baseDir, nickname, domain, postJsonObject)
|
||||
if cachedPostFilename:
|
||||
if os.path.isfile(cachedPostFilename):
|
||||
os.remove(cachedPostFilename)
|
||||
try:
|
||||
os.remove(cachedPostFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
# if the post is in the recent posts cache then mark it as unmuted
|
||||
if recentPostsCache.get('index'):
|
||||
|
|
@ -596,14 +639,40 @@ def unmutePost(baseDir: str, nickname: str, domain: str, port: int,
|
|||
removeIdEnding(postJsonObject['id']).replace('/', '#')
|
||||
if postId in recentPostsCache['index']:
|
||||
print('UNMUTE: ' + postId + ' is in recent posts cache')
|
||||
if recentPostsCache['json'].get(postId):
|
||||
postJsonObject['muted'] = False
|
||||
recentPostsCache['json'][postId] = json.dumps(postJsonObject)
|
||||
if recentPostsCache.get('html'):
|
||||
if recentPostsCache['html'].get(postId):
|
||||
del recentPostsCache['html'][postId]
|
||||
print('UNMUTE: ' + postId +
|
||||
' marked as unmuted in recent posts cache')
|
||||
if recentPostsCache.get('json'):
|
||||
recentPostsCache['json'][postId] = json.dumps(postJsonObject)
|
||||
print('UNMUTE: ' + postId +
|
||||
' marked as unmuted in recent posts cache')
|
||||
if recentPostsCache.get('html'):
|
||||
if recentPostsCache['html'].get(postId):
|
||||
del recentPostsCache['html'][postId]
|
||||
print('UNMUTE: ' + postId + ' removed cached html')
|
||||
if alsoUpdatePostId:
|
||||
postFilename = locatePost(baseDir, nickname, domain, alsoUpdatePostId)
|
||||
if os.path.isfile(postFilename):
|
||||
postJsonObj = loadJson(postFilename)
|
||||
cachedPostFilename = \
|
||||
getCachedPostFilename(baseDir, nickname, domain,
|
||||
postJsonObj)
|
||||
if cachedPostFilename:
|
||||
if os.path.isfile(cachedPostFilename):
|
||||
try:
|
||||
os.remove(cachedPostFilename)
|
||||
print('MUTE: cached referenced post removed ' +
|
||||
cachedPostFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
if recentPostsCache.get('json'):
|
||||
if recentPostsCache['json'].get(alsoUpdatePostId):
|
||||
del recentPostsCache['json'][alsoUpdatePostId]
|
||||
print('UNMUTE: ' +
|
||||
alsoUpdatePostId + ' removed referenced json')
|
||||
if recentPostsCache.get('html'):
|
||||
if recentPostsCache['html'].get(alsoUpdatePostId):
|
||||
del recentPostsCache['html'][alsoUpdatePostId]
|
||||
print('UNMUTE: ' +
|
||||
alsoUpdatePostId + ' removed referenced html')
|
||||
|
||||
|
||||
def outboxMute(baseDir: str, httpPrefix: str,
|
||||
|
|
@ -614,20 +683,14 @@ def outboxMute(baseDir: str, httpPrefix: str,
|
|||
"""
|
||||
if not messageJson.get('type'):
|
||||
return
|
||||
if not messageJson.get('actor'):
|
||||
if not hasActor(messageJson, debug):
|
||||
return
|
||||
domainFull = getFullDomain(domain, port)
|
||||
if not messageJson['actor'].endswith(domainFull + '/users/' + nickname):
|
||||
return
|
||||
if not messageJson['type'] == 'Ignore':
|
||||
return
|
||||
if not messageJson.get('object'):
|
||||
if debug:
|
||||
print('DEBUG: no object in mute')
|
||||
return
|
||||
if not isinstance(messageJson['object'], str):
|
||||
if debug:
|
||||
print('DEBUG: mute object is not string')
|
||||
if not hasObjectString(messageJson, debug):
|
||||
return
|
||||
if debug:
|
||||
print('DEBUG: c2s mute request arrived in outbox')
|
||||
|
|
@ -669,16 +732,14 @@ def outboxUndoMute(baseDir: str, httpPrefix: str,
|
|||
"""
|
||||
if not messageJson.get('type'):
|
||||
return
|
||||
if not messageJson.get('actor'):
|
||||
if not hasActor(messageJson, debug):
|
||||
return
|
||||
domainFull = getFullDomain(domain, port)
|
||||
if not messageJson['actor'].endswith(domainFull + '/users/' + nickname):
|
||||
return
|
||||
if not messageJson['type'] == 'Undo':
|
||||
return
|
||||
if not hasObjectDict(messageJson):
|
||||
return
|
||||
if not messageJson['object'].get('type'):
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if messageJson['object']['type'] != 'Ignore':
|
||||
return
|
||||
|
|
@ -740,7 +801,10 @@ def setBrochMode(baseDir: str, domainFull: str, enabled: bool) -> None:
|
|||
if not enabled:
|
||||
# remove instance allow list
|
||||
if os.path.isfile(allowFilename):
|
||||
os.remove(allowFilename)
|
||||
try:
|
||||
os.remove(allowFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
print('Broch mode turned off')
|
||||
else:
|
||||
if os.path.isfile(allowFilename):
|
||||
|
|
@ -799,11 +863,14 @@ def brochModeLapses(baseDir: str, lapseDays: int = 7) -> bool:
|
|||
currTime = datetime.datetime.utcnow()
|
||||
daysSinceBroch = (currTime - modifiedDate).days
|
||||
if daysSinceBroch >= lapseDays:
|
||||
removed = False
|
||||
try:
|
||||
os.remove(allowFilename)
|
||||
removed = True
|
||||
except BaseException:
|
||||
pass
|
||||
if removed:
|
||||
setConfigParam(baseDir, "brochMode", False)
|
||||
print('Broch mode has elapsed')
|
||||
return True
|
||||
except BaseException:
|
||||
pass
|
||||
return False
|
||||
|
|
|
|||
2
blog.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
|
|
|
|||
80
bookmarks.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Timeline"
|
||||
|
||||
|
|
@ -26,6 +26,8 @@ from utils import saveJson
|
|||
from utils import hasObjectDict
|
||||
from utils import acctDir
|
||||
from utils import localActorUrl
|
||||
from utils import hasActor
|
||||
from utils import hasObjectStringType
|
||||
from posts import getPersonBox
|
||||
from session import postJson
|
||||
|
||||
|
|
@ -47,7 +49,10 @@ def undoBookmarksCollectionEntry(recentPostsCache: {},
|
|||
domain, postJsonObject)
|
||||
if cachedPostFilename:
|
||||
if os.path.isfile(cachedPostFilename):
|
||||
os.remove(cachedPostFilename)
|
||||
try:
|
||||
os.remove(cachedPostFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
removePostFromCache(postJsonObject, recentPostsCache)
|
||||
|
||||
# remove from the index
|
||||
|
|
@ -152,7 +157,10 @@ def updateBookmarksCollection(recentPostsCache: {},
|
|||
domain, postJsonObject)
|
||||
if cachedPostFilename:
|
||||
if os.path.isfile(cachedPostFilename):
|
||||
os.remove(cachedPostFilename)
|
||||
try:
|
||||
os.remove(cachedPostFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
removePostFromCache(postJsonObject, recentPostsCache)
|
||||
|
||||
if not postJsonObject.get('object'):
|
||||
|
|
@ -348,7 +356,8 @@ def sendBookmarkViaServer(baseDir: str, session,
|
|||
domain: str, fromPort: int,
|
||||
httpPrefix: str, bookmarkUrl: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Creates a bookmark via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -377,7 +386,8 @@ def sendBookmarkViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = webfingerHandle(session, handle, httpPrefix,
|
||||
cachedWebfingers,
|
||||
domain, projectVersion, debug, False)
|
||||
domain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: bookmark webfinger failed for ' + handle)
|
||||
|
|
@ -390,12 +400,15 @@ def sendBookmarkViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox,
|
||||
avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
nickname, domain,
|
||||
postToBox, 52594)
|
||||
originDomain = domain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem,
|
||||
originDomain,
|
||||
baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
nickname, domain,
|
||||
postToBox, 58391)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -433,7 +446,8 @@ def sendUndoBookmarkViaServer(baseDir: str, session,
|
|||
domain: str, fromPort: int,
|
||||
httpPrefix: str, bookmarkUrl: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Removes a bookmark via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -462,7 +476,8 @@ def sendUndoBookmarkViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = webfingerHandle(session, handle, httpPrefix,
|
||||
cachedWebfingers,
|
||||
domain, projectVersion, debug, False)
|
||||
domain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: unbookmark webfinger failed for ' + handle)
|
||||
|
|
@ -475,12 +490,15 @@ def sendUndoBookmarkViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox,
|
||||
avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
nickname, domain,
|
||||
postToBox, 52594)
|
||||
originDomain = domain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem,
|
||||
originDomain,
|
||||
baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
nickname, domain,
|
||||
postToBox, 52594)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -523,21 +541,13 @@ def outboxBookmark(recentPostsCache: {},
|
|||
return
|
||||
if messageJson['type'] != 'Add':
|
||||
return
|
||||
if not messageJson.get('actor'):
|
||||
if debug:
|
||||
print('DEBUG: no actor in bookmark Add')
|
||||
return
|
||||
if not hasObjectDict(messageJson):
|
||||
if debug:
|
||||
print('DEBUG: no object in bookmark Add')
|
||||
if not hasActor(messageJson, debug):
|
||||
return
|
||||
if not messageJson.get('target'):
|
||||
if debug:
|
||||
print('DEBUG: no target in bookmark Add')
|
||||
return
|
||||
if not messageJson['object'].get('type'):
|
||||
if debug:
|
||||
print('DEBUG: no object type in bookmark Add')
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if not isinstance(messageJson['target'], str):
|
||||
if debug:
|
||||
|
|
@ -587,21 +597,13 @@ def outboxUndoBookmark(recentPostsCache: {},
|
|||
return
|
||||
if messageJson['type'] != 'Remove':
|
||||
return
|
||||
if not messageJson.get('actor'):
|
||||
if debug:
|
||||
print('DEBUG: no actor in unbookmark Remove')
|
||||
return
|
||||
if not hasObjectDict(messageJson):
|
||||
if debug:
|
||||
print('DEBUG: no object in unbookmark Remove')
|
||||
if not hasActor(messageJson, debug):
|
||||
return
|
||||
if not messageJson.get('target'):
|
||||
if debug:
|
||||
print('DEBUG: no target in unbookmark Remove')
|
||||
return
|
||||
if not messageJson['object'].get('type'):
|
||||
if debug:
|
||||
print('DEBUG: no object type in bookmark Remove')
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if not isinstance(messageJson['target'], str):
|
||||
if debug:
|
||||
|
|
|
|||
2
briar.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
|
|
|
|||
8
cache.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
|
||||
|
|
@ -139,7 +139,8 @@ def getWebfingerFromCache(handle: str, cachedWebfingers: {}) -> {}:
|
|||
def getPersonPubKey(baseDir: str, session, personUrl: str,
|
||||
personCache: {}, debug: bool,
|
||||
projectVersion: str, httpPrefix: str,
|
||||
domain: str, onionDomain: str) -> str:
|
||||
domain: str, onionDomain: str,
|
||||
signingPrivateKeyPem: str) -> str:
|
||||
if not personUrl:
|
||||
return None
|
||||
personUrl = personUrl.replace('#main-key', '')
|
||||
|
|
@ -165,7 +166,8 @@ def getPersonPubKey(baseDir: str, session, personUrl: str,
|
|||
'Accept': 'application/activity+json; profile="' + profileStr + '"'
|
||||
}
|
||||
personJson = \
|
||||
getJson(session, personUrl, asHeader, None, debug,
|
||||
getJson(signingPrivateKeyPem,
|
||||
session, personUrl, asHeader, None, debug,
|
||||
projectVersion, httpPrefix, personDomain)
|
||||
if not personJson:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ example.com {
|
|||
header / X-Download-Options "noopen"
|
||||
header / X-Frame-Options "DENY"
|
||||
header / X-Permitted-Cross-Domain-Policies "none"
|
||||
header / X-Robots-Tag "noindex,nofollow,nosnippet,noarchive"
|
||||
header / X-Robots-Tag "noindex"
|
||||
header / X-XSS-Protection "1; mode=block"
|
||||
|
||||
proxy / http://localhost:7156 {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "RSS Feeds"
|
||||
|
||||
|
|
@ -93,7 +93,10 @@ def updateHashtagCategories(baseDir: str) -> None:
|
|||
hashtagCategories = getHashtagCategories(baseDir)
|
||||
if not hashtagCategories:
|
||||
if os.path.isfile(categoryListFilename):
|
||||
os.remove(categoryListFilename)
|
||||
try:
|
||||
os.remove(categoryListFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
return
|
||||
|
||||
categoryList = []
|
||||
|
|
|
|||
2
city.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Metadata"
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ If you're raising concerns about something or someone, there must be demonstrabl
|
|||
|
||||
This is not a big project and so there is no division of labor or special enforcement committee or bureaucratic process.
|
||||
|
||||
Complaints should be either reported in the Matrix chat room **#epicyon:matrix.freedombone.net** or sent to bob@freedombone.net, preferably via XMPP/Conversations with OMEMO enabled but you can also use the same address for email correspondence.
|
||||
Complaints should be either reported in the Matrix chat room **#epicyon:matrix.libreserver.org** or sent to bob@libreserver.org, preferably via XMPP/Conversations with OMEMO enabled but you can also use the same address for email correspondence.
|
||||
|
||||
## In case of violations
|
||||
|
||||
|
|
@ -60,6 +60,5 @@ Violators of this code of conduct will:
|
|||
|
||||
* Be removed from any associated Matrix and/or XMPP chat rooms
|
||||
* Will not have pending or future patches or pull requests merged
|
||||
* If they have a user account on *code.freedombone.net* it will be removed
|
||||
|
||||
This applies regardless of past levels of commitment or technical abilities.
|
||||
|
|
|
|||
84
content.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ import os
|
|||
import email.parser
|
||||
import urllib.parse
|
||||
from shutil import copyfile
|
||||
from utils import dangerousSVG
|
||||
from utils import removeDomainPort
|
||||
from utils import isValidLanguage
|
||||
from utils import getImageExtensions
|
||||
|
|
@ -23,6 +24,7 @@ from utils import containsPGPPublicKey
|
|||
from utils import acctDir
|
||||
from utils import isfloat
|
||||
from utils import getCurrencies
|
||||
from utils import removeHtml
|
||||
from petnames import getPetName
|
||||
|
||||
|
||||
|
|
@ -180,8 +182,8 @@ def dangerousCSS(filename: str, allowLocalNetworkAccess: bool) -> bool:
|
|||
cssMatches = ('behavior:', ':expression', '?php', '.php',
|
||||
'google', 'regexp', 'localhost',
|
||||
'127.0.', '192.168', '10.0.', '@import')
|
||||
for match in cssMatches:
|
||||
if match in content:
|
||||
for cssmatch in cssMatches:
|
||||
if cssmatch in content:
|
||||
return True
|
||||
|
||||
# search for non-local web links
|
||||
|
|
@ -726,8 +728,8 @@ def _autoTag(baseDir: str, nickname: str, domain: str,
|
|||
continue
|
||||
if '->' not in tagRule:
|
||||
continue
|
||||
match = tagRule.split('->')[0].strip()
|
||||
if match != wordStr:
|
||||
rulematch = tagRule.split('->')[0].strip()
|
||||
if rulematch != wordStr:
|
||||
continue
|
||||
tagName = tagRule.split('->')[1].strip()
|
||||
if tagName.startswith('#'):
|
||||
|
|
@ -938,9 +940,15 @@ def saveMediaInFormPOST(mediaBytes, debug: bool,
|
|||
for ex in extensionTypes:
|
||||
possibleOtherFormat = filenameBase + '.' + ex
|
||||
if os.path.isfile(possibleOtherFormat):
|
||||
os.remove(possibleOtherFormat)
|
||||
try:
|
||||
os.remove(possibleOtherFormat)
|
||||
except BaseException:
|
||||
pass
|
||||
if os.path.isfile(filenameBase):
|
||||
os.remove(filenameBase)
|
||||
try:
|
||||
os.remove(filenameBase)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
if debug:
|
||||
print('DEBUG: No media found within POST')
|
||||
|
|
@ -1006,7 +1014,17 @@ def saveMediaInFormPOST(mediaBytes, debug: bool,
|
|||
detectedExtension, '.' +
|
||||
ex)
|
||||
if os.path.isfile(possibleOtherFormat):
|
||||
os.remove(possibleOtherFormat)
|
||||
try:
|
||||
os.remove(possibleOtherFormat)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
# don't allow scripts within svg files
|
||||
if detectedExtension == 'svg':
|
||||
svgStr = mediaBytes[startPos:]
|
||||
svgStr = svgStr.decode()
|
||||
if dangerousSVG(svgStr, False):
|
||||
return None, None
|
||||
|
||||
with open(filename, 'wb') as fp:
|
||||
fp.write(mediaBytes[startPos:])
|
||||
|
|
@ -1116,3 +1134,53 @@ def getPriceFromString(priceStr: str) -> (str, str):
|
|||
if isfloat(priceStr):
|
||||
return priceStr, "EUR"
|
||||
return "0.00", "EUR"
|
||||
|
||||
|
||||
def _wordsSimilarityHistogram(words: []) -> {}:
|
||||
"""Returns a histogram for word combinations
|
||||
"""
|
||||
histogram = {}
|
||||
for index in range(1, len(words)):
|
||||
combinedWords = words[index - 1] + words[index]
|
||||
if histogram.get(combinedWords):
|
||||
histogram[combinedWords] += 1
|
||||
else:
|
||||
histogram[combinedWords] = 1
|
||||
return histogram
|
||||
|
||||
|
||||
def _wordsSimilarityWordsList(content: str) -> []:
|
||||
"""Returns a list of words for the given content
|
||||
"""
|
||||
removePunctuation = ('.', ',', ';', '-', ':', '"')
|
||||
content = removeHtml(content).lower()
|
||||
for p in removePunctuation:
|
||||
content = content.replace(p, ' ')
|
||||
content = content.replace(' ', ' ')
|
||||
return content.split(' ')
|
||||
|
||||
|
||||
def wordsSimilarity(content1: str, content2: str, minWords: int) -> int:
|
||||
"""Returns percentage similarity
|
||||
"""
|
||||
if content1 == content2:
|
||||
return 100
|
||||
|
||||
words1 = _wordsSimilarityWordsList(content1)
|
||||
if len(words1) < minWords:
|
||||
return 0
|
||||
|
||||
words2 = _wordsSimilarityWordsList(content2)
|
||||
if len(words2) < minWords:
|
||||
return 0
|
||||
|
||||
histogram1 = _wordsSimilarityHistogram(words1)
|
||||
histogram2 = _wordsSimilarityHistogram(words2)
|
||||
|
||||
diff = 0
|
||||
for combinedWords, hits in histogram1.items():
|
||||
if not histogram2.get(combinedWords):
|
||||
diff += 1
|
||||
else:
|
||||
diff += abs(histogram2[combinedWords] - histogram1[combinedWords])
|
||||
return 100 - int(diff * 100 / len(histogram1.items()))
|
||||
|
|
|
|||
20
context.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Security"
|
||||
|
||||
|
|
@ -20,6 +20,24 @@ validContexts = (
|
|||
)
|
||||
|
||||
|
||||
def getIndividualPostContext() -> []:
|
||||
"""Returns the context for an individual post
|
||||
"""
|
||||
return [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount",
|
||||
"blurhash": "toot:blurhash"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def hasValidContext(postJsonObject: {}) -> bool:
|
||||
"""Are the links within the @context of a post recognised?
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -3,32 +3,60 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Timeline"
|
||||
|
||||
import os
|
||||
from utils import hasObjectDict
|
||||
from utils import acctDir
|
||||
from utils import removeIdEnding
|
||||
|
||||
|
||||
def _getConversationFilename(baseDir: str, nickname: str, domain: str,
|
||||
postJsonObject: {}) -> str:
|
||||
"""Returns the conversation filename
|
||||
"""
|
||||
if not hasObjectDict(postJsonObject):
|
||||
return None
|
||||
if not postJsonObject['object'].get('conversation'):
|
||||
return None
|
||||
if not postJsonObject['object'].get('id'):
|
||||
return None
|
||||
conversationDir = acctDir(baseDir, nickname, domain) + '/conversation'
|
||||
if not os.path.isdir(conversationDir):
|
||||
os.mkdir(conversationDir)
|
||||
conversationId = postJsonObject['object']['conversation']
|
||||
conversationId = conversationId.replace('/', '#')
|
||||
return conversationDir + '/' + conversationId
|
||||
|
||||
|
||||
def previousConversationPostId(baseDir: str, nickname: str, domain: str,
|
||||
postJsonObject: {}) -> str:
|
||||
"""Returns the previous conversation post id
|
||||
"""
|
||||
conversationFilename = \
|
||||
_getConversationFilename(baseDir, nickname, domain, postJsonObject)
|
||||
if not conversationFilename:
|
||||
return False
|
||||
if not os.path.isfile(conversationFilename):
|
||||
return False
|
||||
with open(conversationFilename, 'r') as fp:
|
||||
lines = fp.readlines()
|
||||
if lines:
|
||||
return lines[-1].replace('\n', '')
|
||||
return False
|
||||
|
||||
|
||||
def updateConversation(baseDir: str, nickname: str, domain: str,
|
||||
postJsonObject: {}) -> bool:
|
||||
"""Ads a post to a conversation index in the /conversation subdirectory
|
||||
"""
|
||||
if not hasObjectDict(postJsonObject):
|
||||
conversationFilename = \
|
||||
_getConversationFilename(baseDir, nickname, domain, postJsonObject)
|
||||
if not conversationFilename:
|
||||
return False
|
||||
if not postJsonObject['object'].get('conversation'):
|
||||
return False
|
||||
if not postJsonObject['object'].get('id'):
|
||||
return False
|
||||
conversationDir = acctDir(baseDir, nickname, domain) + '/conversation'
|
||||
if not os.path.isdir(conversationDir):
|
||||
os.mkdir(conversationDir)
|
||||
conversationId = postJsonObject['object']['conversation']
|
||||
conversationId = conversationId.replace('/', '#')
|
||||
postId = postJsonObject['object']['id']
|
||||
conversationFilename = conversationDir + '/' + conversationId
|
||||
postId = removeIdEnding(postJsonObject['object']['id'])
|
||||
if not os.path.isfile(conversationFilename):
|
||||
try:
|
||||
with open(conversationFilename, 'w+') as fp:
|
||||
|
|
|
|||
2
cwtch.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Posts can be removed on request if there is sufficient justification, but the na
|
|||
### Content Policy
|
||||
This instance will not host content containing sexism, racism, casteism, homophobia, transphobia, misogyny, antisemitism or other forms of bigotry or discrimination on the basis of nationality or immigration status. Claims that transgressions of this type were intended to be "ironic" will be treated as a terms of service violation.
|
||||
|
||||
Even if not conspicuously discriminatory, expressions of support for organizations with discrminatory agendas are not permitted on this instance. These include, but are not limited to, racial supremacist groups, the redpill/incel movement and anti-LGBT or anti-immigrant campaigns.
|
||||
Even if not conspicuously discriminatory, expressions of support for organizations with discrminatory agendas are not permitted on this instance. These include, but are not limited to, racial supremacist groups, the redpill/incel movement, anti-vaccination, anti-LGBT and anti-immigrant campaigns.
|
||||
|
||||
Depictions of injury, death or medical procedures are not permitted.
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ Moderators rely upon your reports. Don't assume that something of concern has al
|
|||
Content found to be non-compliant with this policy will be removed and any accounts on this instance producing, repeating or linking to such content will be deleted typically without prior notification.
|
||||
|
||||
### Federation Policy
|
||||
In a proactive effort to avoid the classic fate of *"embrace, extend, extinguish"* this system will block any instance launched, acquired or funded by Alphabet, Facebook, Twitter, Microsoft, Apple, Amazon, Elsevier or other monopolistic Silicon Valley companies.
|
||||
In a proactive effort to avoid the classic fate of *"embrace, extend, extinguish"* this system will block any instance launched, acquired or funded by Alphabet/Google, Facebook, Twitter, Microsoft, Apple, Amazon, Elsevier or other monopolistic Silicon Valley companies.
|
||||
|
||||
This system will not federate with instances whose moderation policy is incompatible with the content policy described above. If an instance lacks a moderation policy, or refuses to enforce one, it will be assumed to be incompatible.
|
||||
|
||||
|
|
|
|||
25
delete.py
|
|
@ -3,12 +3,13 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
import os
|
||||
from datetime import datetime
|
||||
from utils import hasObjectString
|
||||
from utils import removeDomainPort
|
||||
from utils import hasUsersPath
|
||||
from utils import getFullDomain
|
||||
|
|
@ -30,7 +31,8 @@ def sendDeleteViaServer(baseDir: str, session,
|
|||
fromDomain: str, fromPort: int,
|
||||
httpPrefix: str, deleteObjectUrl: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Creates a delete request message via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -57,7 +59,8 @@ def sendDeleteViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = \
|
||||
webfingerHandle(session, handle, httpPrefix, cachedWebfingers,
|
||||
fromDomain, projectVersion, debug, False)
|
||||
fromDomain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: delete webfinger failed for ' + handle)
|
||||
|
|
@ -70,11 +73,13 @@ def sendDeleteViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
originDomain = fromDomain
|
||||
(inboxUrl, pubKeyId, pubKey,
|
||||
fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName) = getPersonBox(baseDir, session, wfRequest, personCache,
|
||||
projectVersion, httpPrefix, fromNickname,
|
||||
fromDomain, postToBox, 53036)
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem, originDomain,
|
||||
baseDir, session, wfRequest, personCache,
|
||||
projectVersion, httpPrefix, fromNickname,
|
||||
fromDomain, postToBox, 53036)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -122,13 +127,7 @@ def outboxDelete(baseDir: str, httpPrefix: str,
|
|||
if debug:
|
||||
print('DEBUG: not a delete')
|
||||
return
|
||||
if not messageJson.get('object'):
|
||||
if debug:
|
||||
print('DEBUG: no object in delete')
|
||||
return
|
||||
if not isinstance(messageJson['object'], str):
|
||||
if debug:
|
||||
print('DEBUG: delete object is not string')
|
||||
if not hasObjectString(messageJson, debug):
|
||||
return
|
||||
if debug:
|
||||
print('DEBUG: c2s delete request arrived in outbox')
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Client"
|
||||
|
||||
|
|
@ -418,7 +418,8 @@ def _desktopReplyToPost(session, postId: str,
|
|||
debug: bool, subject: str,
|
||||
screenreader: str, systemLanguage: str,
|
||||
espeak, conversationId: str,
|
||||
lowBandwidth: bool) -> None:
|
||||
lowBandwidth: bool,
|
||||
signingPrivateKeyPem: str) -> None:
|
||||
"""Use the desktop client to send a reply to the most recent post
|
||||
"""
|
||||
if '://' not in postId:
|
||||
|
|
@ -463,7 +464,7 @@ def _desktopReplyToPost(session, postId: str,
|
|||
city = 'London, England'
|
||||
sayStr = 'Sending reply'
|
||||
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
||||
if sendPostViaServer(__version__,
|
||||
if sendPostViaServer(signingPrivateKeyPem, __version__,
|
||||
baseDir, session, nickname, password,
|
||||
domain, port,
|
||||
toNickname, toDomain, toPort, ccUrl,
|
||||
|
|
@ -486,7 +487,8 @@ def _desktopNewPost(session,
|
|||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool,
|
||||
screenreader: str, systemLanguage: str,
|
||||
espeak, lowBandwidth: bool) -> None:
|
||||
espeak, lowBandwidth: bool,
|
||||
signingPrivateKeyPem: str) -> None:
|
||||
"""Use the desktop client to create a new post
|
||||
"""
|
||||
conversationId = None
|
||||
|
|
@ -527,7 +529,7 @@ def _desktopNewPost(session,
|
|||
subject = None
|
||||
sayStr = 'Sending'
|
||||
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
||||
if sendPostViaServer(__version__,
|
||||
if sendPostViaServer(signingPrivateKeyPem, __version__,
|
||||
baseDir, session, nickname, password,
|
||||
domain, port,
|
||||
None, '#Public', port, ccUrl,
|
||||
|
|
@ -661,7 +663,9 @@ def _readLocalBoxPost(session, nickname: str, domain: str,
|
|||
systemLanguage: str,
|
||||
screenreader: str, espeak,
|
||||
translate: {}, yourActor: str,
|
||||
domainFull: str, personCache: {}) -> {}:
|
||||
domainFull: str, personCache: {},
|
||||
signingPrivateKeyPem: str,
|
||||
blockedCache: {}) -> {}:
|
||||
"""Reads a post from the given timeline
|
||||
Returns the post json
|
||||
"""
|
||||
|
|
@ -688,6 +692,7 @@ def _readLocalBoxPost(session, nickname: str, domain: str,
|
|||
recentPostsCache = {}
|
||||
allowLocalNetworkAccess = False
|
||||
YTReplacementDomain = None
|
||||
twitterReplacementDomain = None
|
||||
postJsonObject2 = \
|
||||
downloadAnnounce(session, baseDir,
|
||||
httpPrefix,
|
||||
|
|
@ -695,10 +700,13 @@ def _readLocalBoxPost(session, nickname: str, domain: str,
|
|||
postJsonObject,
|
||||
__version__, translate,
|
||||
YTReplacementDomain,
|
||||
twitterReplacementDomain,
|
||||
allowLocalNetworkAccess,
|
||||
recentPostsCache, False,
|
||||
systemLanguage,
|
||||
domainFull, personCache)
|
||||
domainFull, personCache,
|
||||
signingPrivateKeyPem,
|
||||
blockedCache)
|
||||
if postJsonObject2:
|
||||
if hasObjectDict(postJsonObject2):
|
||||
if postJsonObject2['object'].get('attributedTo') and \
|
||||
|
|
@ -742,7 +750,7 @@ def _readLocalBoxPost(session, nickname: str, domain: str,
|
|||
if isPGPEncrypted(content):
|
||||
sayStr = 'Encrypted message. Please enter your passphrase.'
|
||||
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
||||
content = pgpDecrypt(domain, content, actor)
|
||||
content = pgpDecrypt(domain, content, actor, signingPrivateKeyPem)
|
||||
if isPGPEncrypted(content):
|
||||
sayStr = 'Message could not be decrypted'
|
||||
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
||||
|
|
@ -823,7 +831,7 @@ def _desktopShowProfile(session, nickname: str, domain: str,
|
|||
systemLanguage: str,
|
||||
screenreader: str, espeak,
|
||||
translate: {}, yourActor: str,
|
||||
postJsonObject: {}) -> {}:
|
||||
postJsonObject: {}, signingPrivateKeyPem: str) -> {}:
|
||||
"""Shows the profile of the actor for the given post
|
||||
Returns the actor json
|
||||
"""
|
||||
|
|
@ -854,7 +862,8 @@ def _desktopShowProfile(session, nickname: str, domain: str,
|
|||
if 'http://' in actor:
|
||||
isHttp = True
|
||||
actorJson, asHeader = \
|
||||
getActorJson(domain, actor, isHttp, False, False, True)
|
||||
getActorJson(domain, actor, isHttp, False, False, True,
|
||||
signingPrivateKeyPem)
|
||||
|
||||
_desktopShowActor(baseDir, actorJson, translate,
|
||||
systemLanguage, screenreader, espeak)
|
||||
|
|
@ -868,12 +877,14 @@ def _desktopShowProfileFromHandle(session, nickname: str, domain: str,
|
|||
systemLanguage: str,
|
||||
screenreader: str, espeak,
|
||||
translate: {}, yourActor: str,
|
||||
postJsonObject: {}) -> {}:
|
||||
postJsonObject: {},
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Shows the profile for a handle
|
||||
Returns the actor json
|
||||
"""
|
||||
actorJson, asHeader = \
|
||||
getActorJson(domain, handle, False, False, False, True)
|
||||
getActorJson(domain, handle, False, False, False, True,
|
||||
signingPrivateKeyPem)
|
||||
|
||||
_desktopShowActor(baseDir, actorJson, translate,
|
||||
systemLanguage, screenreader, espeak)
|
||||
|
|
@ -1112,7 +1123,8 @@ def _desktopNewDM(session, toHandle: str,
|
|||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool,
|
||||
screenreader: str, systemLanguage: str,
|
||||
espeak, lowBandwidth: bool) -> None:
|
||||
espeak, lowBandwidth: bool,
|
||||
signingPrivateKeyPem: str) -> None:
|
||||
"""Use the desktop client to create a new direct message
|
||||
which can include multiple destination handles
|
||||
"""
|
||||
|
|
@ -1133,7 +1145,8 @@ def _desktopNewDM(session, toHandle: str,
|
|||
cachedWebfingers, personCache,
|
||||
debug,
|
||||
screenreader, systemLanguage,
|
||||
espeak, lowBandwidth)
|
||||
espeak, lowBandwidth,
|
||||
signingPrivateKeyPem)
|
||||
|
||||
|
||||
def _desktopNewDMbase(session, toHandle: str,
|
||||
|
|
@ -1142,7 +1155,8 @@ def _desktopNewDMbase(session, toHandle: str,
|
|||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool,
|
||||
screenreader: str, systemLanguage: str,
|
||||
espeak, lowBandwidth: bool) -> None:
|
||||
espeak, lowBandwidth: bool,
|
||||
signingPrivateKeyPem: str) -> None:
|
||||
"""Use the desktop client to create a new direct message
|
||||
"""
|
||||
conversationId = None
|
||||
|
|
@ -1201,7 +1215,8 @@ def _desktopNewDMbase(session, toHandle: str,
|
|||
for after in range(randint(1, 16)):
|
||||
paddedMessage += ' '
|
||||
cipherText = \
|
||||
pgpEncryptToActor(domain, paddedMessage, toHandle)
|
||||
pgpEncryptToActor(domain, paddedMessage, toHandle,
|
||||
signingPrivateKeyPem)
|
||||
if not cipherText:
|
||||
sayStr = \
|
||||
toHandle + ' has no PGP public key. ' + \
|
||||
|
|
@ -1222,7 +1237,7 @@ def _desktopNewDMbase(session, toHandle: str,
|
|||
|
||||
sayStr = 'Sending'
|
||||
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
||||
if sendPostViaServer(__version__,
|
||||
if sendPostViaServer(signingPrivateKeyPem, __version__,
|
||||
baseDir, session, nickname, password,
|
||||
domain, port,
|
||||
toNickname, toDomain, toPort, ccUrl,
|
||||
|
|
@ -1301,6 +1316,11 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
"""Runs the desktop and screen reader client,
|
||||
which announces new inbox items
|
||||
"""
|
||||
# TODO: this should probably be retrieved somehow from the server
|
||||
signingPrivateKeyPem = None
|
||||
|
||||
blockedCache = {}
|
||||
|
||||
indent = ' '
|
||||
if showNewPosts:
|
||||
indent = ''
|
||||
|
|
@ -1400,7 +1420,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
nickname, password,
|
||||
domain, port, httpPrefix,
|
||||
cachedWebfingers, personCache,
|
||||
debug, False)
|
||||
debug, False,
|
||||
signingPrivateKeyPem)
|
||||
sayStr = indent + 'PGP public key uploaded'
|
||||
_sayCommand(sayStr, sayStr, screenreader,
|
||||
systemLanguage, espeak)
|
||||
|
|
@ -1410,7 +1431,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
nickname, password,
|
||||
domain, port, httpPrefix,
|
||||
currTimeline, pageNumber,
|
||||
debug)
|
||||
debug, signingPrivateKeyPem)
|
||||
|
||||
followRequestsJson = \
|
||||
getFollowRequestsViaServer(baseDir, session,
|
||||
|
|
@ -1418,14 +1439,16 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port,
|
||||
httpPrefix, 1,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
|
||||
if not (currTimeline == 'inbox' and pageNumber == 1):
|
||||
# monitor the inbox to generate notifications
|
||||
inboxJson = c2sBoxJson(baseDir, session,
|
||||
nickname, password,
|
||||
domain, port, httpPrefix,
|
||||
'inbox', 1, debug)
|
||||
'inbox', 1, debug,
|
||||
signingPrivateKeyPem)
|
||||
else:
|
||||
inboxJson = boxJson
|
||||
newDMsExist = False
|
||||
|
|
@ -1502,7 +1525,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
nickname, password,
|
||||
domain, port, httpPrefix,
|
||||
currTimeline, pageNumber,
|
||||
debug)
|
||||
debug, signingPrivateKeyPem)
|
||||
if boxJson:
|
||||
_desktopShowBox(indent, followRequestsJson,
|
||||
yourActor, currTimeline, boxJson,
|
||||
|
|
@ -1519,7 +1542,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
nickname, password,
|
||||
domain, port, httpPrefix,
|
||||
currTimeline, pageNumber,
|
||||
debug)
|
||||
debug, signingPrivateKeyPem)
|
||||
if boxJson:
|
||||
_desktopShowBox(indent, followRequestsJson,
|
||||
yourActor, currTimeline, boxJson,
|
||||
|
|
@ -1537,7 +1560,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
nickname, password,
|
||||
domain, port, httpPrefix,
|
||||
currTimeline, pageNumber,
|
||||
debug)
|
||||
debug, signingPrivateKeyPem)
|
||||
if boxJson:
|
||||
_desktopShowBox(indent, followRequestsJson,
|
||||
yourActor, currTimeline, boxJson,
|
||||
|
|
@ -1556,7 +1579,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
nickname, password,
|
||||
domain, port, httpPrefix,
|
||||
currTimeline, pageNumber,
|
||||
debug)
|
||||
debug, signingPrivateKeyPem)
|
||||
if boxJson:
|
||||
_desktopShowBox(indent, followRequestsJson,
|
||||
yourActor, currTimeline, boxJson,
|
||||
|
|
@ -1583,7 +1606,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
nickname, password,
|
||||
domain, port, httpPrefix,
|
||||
currTimeline, pageNumber,
|
||||
debug)
|
||||
debug, signingPrivateKeyPem)
|
||||
if boxJson:
|
||||
_desktopShowBox(indent, followRequestsJson,
|
||||
yourActor, currTimeline, boxJson,
|
||||
|
|
@ -1606,7 +1629,9 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
pageNumber, postIndex, boxJson,
|
||||
systemLanguage, screenreader,
|
||||
espeak, translate, yourActor,
|
||||
domainFull, personCache)
|
||||
domainFull, personCache,
|
||||
signingPrivateKeyPem,
|
||||
blockedCache)
|
||||
print('')
|
||||
sayStr = 'Press Enter to continue...'
|
||||
sayStr2 = _highlightText(sayStr)
|
||||
|
|
@ -1628,7 +1653,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
boxJson,
|
||||
systemLanguage, screenreader,
|
||||
espeak, translate, yourActor,
|
||||
postJsonObject)
|
||||
postJsonObject,
|
||||
signingPrivateKeyPem)
|
||||
else:
|
||||
postIndexStr = '1'
|
||||
else:
|
||||
|
|
@ -1643,7 +1669,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
currTimeline, profileHandle,
|
||||
systemLanguage, screenreader,
|
||||
espeak, translate, yourActor,
|
||||
None)
|
||||
None, signingPrivateKeyPem)
|
||||
sayStr = 'Press Enter to continue...'
|
||||
sayStr2 = _highlightText(sayStr)
|
||||
_sayCommand(sayStr2, sayStr,
|
||||
|
|
@ -1661,7 +1687,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
pageNumber, postIndex, boxJson,
|
||||
systemLanguage, screenreader,
|
||||
espeak, translate, yourActor,
|
||||
None)
|
||||
None, signingPrivateKeyPem)
|
||||
sayStr = 'Press Enter to continue...'
|
||||
sayStr2 = _highlightText(sayStr)
|
||||
_sayCommand(sayStr2, sayStr,
|
||||
|
|
@ -1689,7 +1715,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
debug, subject,
|
||||
screenreader, systemLanguage,
|
||||
espeak, conversationId,
|
||||
lowBandwidth)
|
||||
lowBandwidth,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr == 'post' or commandStr == 'p' or
|
||||
|
|
@ -1723,7 +1750,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
cachedWebfingers, personCache,
|
||||
debug,
|
||||
screenreader, systemLanguage,
|
||||
espeak, lowBandwidth)
|
||||
espeak, lowBandwidth,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
else:
|
||||
# public post
|
||||
|
|
@ -1733,7 +1761,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
cachedWebfingers, personCache,
|
||||
debug,
|
||||
screenreader, systemLanguage,
|
||||
espeak, lowBandwidth)
|
||||
espeak, lowBandwidth,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif commandStr == 'like' or commandStr.startswith('like '):
|
||||
|
|
@ -1759,7 +1788,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port, httpPrefix,
|
||||
postJsonObject['id'],
|
||||
cachedWebfingers, personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr == 'undo mute' or
|
||||
|
|
@ -1797,7 +1827,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port,
|
||||
httpPrefix, postJsonObject['id'],
|
||||
cachedWebfingers, personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr == 'mute' or
|
||||
|
|
@ -1826,7 +1857,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port,
|
||||
httpPrefix, postJsonObject['id'],
|
||||
cachedWebfingers, personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr == 'undo bookmark' or
|
||||
|
|
@ -1867,7 +1899,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
postJsonObject['id'],
|
||||
cachedWebfingers,
|
||||
personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr == 'bookmark' or
|
||||
|
|
@ -1896,7 +1929,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port, httpPrefix,
|
||||
postJsonObject['id'],
|
||||
cachedWebfingers, personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr.startswith('undo block ') or
|
||||
|
|
@ -1931,7 +1965,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
blockActor,
|
||||
cachedWebfingers,
|
||||
personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif commandStr.startswith('block '):
|
||||
|
|
@ -1976,7 +2011,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
blockActor,
|
||||
cachedWebfingers,
|
||||
personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif commandStr == 'unlike' or commandStr == 'undo like':
|
||||
|
|
@ -2003,7 +2039,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port, httpPrefix,
|
||||
postJsonObject['id'],
|
||||
cachedWebfingers, personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr.startswith('announce') or
|
||||
|
|
@ -2033,7 +2070,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port,
|
||||
httpPrefix, postId,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr.startswith('unannounce') or
|
||||
|
|
@ -2067,7 +2105,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
httpPrefix, postId,
|
||||
cachedWebfingers,
|
||||
personCache,
|
||||
True, __version__)
|
||||
True, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
elif (commandStr == 'follow requests' or
|
||||
|
|
@ -2083,7 +2122,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port,
|
||||
httpPrefix, currPage,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
if followRequestsJson:
|
||||
if isinstance(followRequestsJson, dict):
|
||||
_desktopShowFollowRequests(followRequestsJson,
|
||||
|
|
@ -2102,7 +2142,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port,
|
||||
httpPrefix, currPage,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
if followingJson:
|
||||
if isinstance(followingJson, dict):
|
||||
_desktopShowFollowing(followingJson, translate,
|
||||
|
|
@ -2122,7 +2163,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
domain, port,
|
||||
httpPrefix, currPage,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
if followersJson:
|
||||
if isinstance(followersJson, dict):
|
||||
_desktopShowFollowing(followersJson, translate,
|
||||
|
|
@ -2161,7 +2203,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
httpPrefix,
|
||||
cachedWebfingers,
|
||||
personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
else:
|
||||
if followHandle:
|
||||
sayStr = followHandle + ' is not valid'
|
||||
|
|
@ -2195,7 +2238,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
httpPrefix,
|
||||
cachedWebfingers,
|
||||
personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
else:
|
||||
sayStr = followHandle + ' is not valid'
|
||||
_sayCommand(sayStr, sayStr,
|
||||
|
|
@ -2224,7 +2268,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
cachedWebfingers,
|
||||
personCache,
|
||||
debug,
|
||||
__version__)
|
||||
__version__,
|
||||
signingPrivateKeyPem)
|
||||
else:
|
||||
if approveHandle:
|
||||
sayStr = approveHandle + ' is not valid'
|
||||
|
|
@ -2256,7 +2301,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
cachedWebfingers,
|
||||
personCache,
|
||||
debug,
|
||||
__version__)
|
||||
__version__,
|
||||
signingPrivateKeyPem)
|
||||
else:
|
||||
if denyHandle:
|
||||
sayStr = denyHandle + ' is not valid'
|
||||
|
|
@ -2331,6 +2377,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
recentPostsCache = {}
|
||||
allowLocalNetworkAccess = False
|
||||
YTReplacementDomain = None
|
||||
twitterReplacementDomain = None
|
||||
postJsonObject2 = \
|
||||
downloadAnnounce(session, baseDir,
|
||||
httpPrefix,
|
||||
|
|
@ -2338,10 +2385,13 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
postJsonObject,
|
||||
__version__, translate,
|
||||
YTReplacementDomain,
|
||||
twitterReplacementDomain,
|
||||
allowLocalNetworkAccess,
|
||||
recentPostsCache, False,
|
||||
systemLanguage,
|
||||
domainFull, personCache)
|
||||
domainFull, personCache,
|
||||
signingPrivateKeyPem,
|
||||
blockedCache)
|
||||
if postJsonObject2:
|
||||
postJsonObject = postJsonObject2
|
||||
if postJsonObject:
|
||||
|
|
@ -2423,7 +2473,8 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|||
postJsonObject['id'],
|
||||
cachedWebfingers,
|
||||
personCache,
|
||||
False, __version__)
|
||||
False, __version__,
|
||||
signingPrivateKeyPem)
|
||||
refreshTimeline = True
|
||||
print('')
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Security"
|
||||
|
||||
|
|
@ -44,7 +44,10 @@ def E2EEremoveDevice(baseDir: str, nickname: str, domain: str,
|
|||
personDir = acctDir(baseDir, nickname, domain)
|
||||
deviceFilename = personDir + '/devices/' + deviceId + '.json'
|
||||
if os.path.isfile(deviceFilename):
|
||||
os.remove(deviceFilename)
|
||||
try:
|
||||
os.remove(deviceFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
|
|
|
|||
|
|
@ -769,5 +769,7 @@
|
|||
"void": "void",
|
||||
"openbsd": "openbsd",
|
||||
"freebsd": "freebsd",
|
||||
"orgmode": "orgmode"
|
||||
"orgmode": "orgmode",
|
||||
"kde": "kde",
|
||||
"ohno": "ohno"
|
||||
}
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
|
@ -201,6 +201,13 @@ figure {
|
|||
height: auto;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: var(--main-bg-color);
|
||||
color: var(--main-fg-color);
|
||||
font-size: 130%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.accesskeys {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
|
|
@ -1383,6 +1390,26 @@ div.container {
|
|||
margin-bottom: var(--button-bottom-margin);
|
||||
margin-left: var(--button-left-margin);
|
||||
}
|
||||
.contactbutton {
|
||||
border-radius: var(--button-corner-radius);
|
||||
background-color: var(--button-background);
|
||||
color: var(--button-text);
|
||||
text-align: center;
|
||||
font-size: var(--font-size-header);
|
||||
font-family: var(--header-font);
|
||||
padding: var(--button-height-padding);
|
||||
width: 20%;
|
||||
margin: var(--button-margin);
|
||||
min-width: var(--button-width-chars);
|
||||
transition: all 0.5s;
|
||||
cursor: pointer;
|
||||
border-top: var(--tab-border-width) solid var(--tab-border-color);
|
||||
border-bottom: none;
|
||||
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
||||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||
margin-bottom: var(--button-bottom-margin);
|
||||
margin-left: var(--button-left-margin);
|
||||
}
|
||||
.buttonDesktop {
|
||||
border-radius: var(--button-corner-radius);
|
||||
background-color: var(--button-background);
|
||||
|
|
@ -1667,6 +1694,8 @@ div.container {
|
|||
.columnIcons img {
|
||||
float: right;
|
||||
}
|
||||
.pageslist {
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2200px) {
|
||||
|
|
@ -1696,7 +1725,7 @@ div.container {
|
|||
color: var(--title-color);
|
||||
}
|
||||
blockquote {
|
||||
font-size: var(--quote-font-size-mobile);
|
||||
font-size: var(--quote-font-size-mobile);
|
||||
}
|
||||
.accountsTable {
|
||||
width: 100%;
|
||||
|
|
@ -2017,6 +2046,25 @@ div.container {
|
|||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||
margin-bottom: var(--button-bottom-margin);
|
||||
}
|
||||
.contactbutton {
|
||||
border-radius: var(--button-corner-radius);
|
||||
background-color: var(--button-background);
|
||||
color: var(--button-text);
|
||||
text-align: center;
|
||||
font-size: var(--font-size-button-mobile);
|
||||
font-family: var(--header-font);
|
||||
padding: var(--button-height-padding-mobile);
|
||||
width: 30%;
|
||||
min-width: var(--button-width-chars);
|
||||
transition: all 0.5s;
|
||||
cursor: pointer;
|
||||
margin: var(--button-margin);
|
||||
border-top: var(--tab-border-width) solid var(--tab-border-color);
|
||||
border-bottom: none;
|
||||
border-left: var(--tab-border-width) solid var(--tab-border-color);
|
||||
border-right: var(--tab-border-width) solid var(--tab-border-color);
|
||||
margin-bottom: var(--button-bottom-margin);
|
||||
}
|
||||
.frontPageMobileButtons{
|
||||
display: block;
|
||||
border: var(--border-width-header) solid var(--border-color);
|
||||
|
|
@ -2323,4 +2371,6 @@ div.container {
|
|||
float: right;
|
||||
margin-right: 1vw;
|
||||
}
|
||||
.pageslist {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
333
epicyon.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Commandline Interface"
|
||||
|
||||
|
|
@ -25,6 +25,7 @@ from roles import setRole
|
|||
from webfinger import webfingerHandle
|
||||
from bookmarks import sendBookmarkViaServer
|
||||
from bookmarks import sendUndoBookmarkViaServer
|
||||
from posts import getInstanceActorKey
|
||||
from posts import sendMuteViaServer
|
||||
from posts import sendUndoMuteViaServer
|
||||
from posts import c2sBoxJson
|
||||
|
|
@ -169,6 +170,11 @@ parser.add_argument('--dormantMonths',
|
|||
default=3,
|
||||
help='How many months does a followed account need to ' +
|
||||
'be unseen for before being considered dormant')
|
||||
parser.add_argument('--defaultReplyIntervalHours',
|
||||
dest='defaultReplyIntervalHours', type=int,
|
||||
default=1000,
|
||||
help='How many hours after publication of a post ' +
|
||||
'are replies to it permitted')
|
||||
parser.add_argument('--sendThreadsTimeoutMins',
|
||||
dest='sendThreadsTimeoutMins', type=int,
|
||||
default=30,
|
||||
|
|
@ -217,6 +223,9 @@ parser.add_argument('--path', dest='baseDir',
|
|||
parser.add_argument('--ytdomain', dest='YTReplacementDomain',
|
||||
type=str, default=None,
|
||||
help='Domain used to replace youtube.com')
|
||||
parser.add_argument('--twitterdomain', dest='twitterReplacementDomain',
|
||||
type=str, default=None,
|
||||
help='Domain used to replace twitter.com')
|
||||
parser.add_argument('--language', dest='language',
|
||||
type=str, default=None,
|
||||
help='Language code, eg. en/fr/de/es')
|
||||
|
|
@ -406,10 +415,11 @@ parser.add_argument("--debug", type=str2bool, nargs='?',
|
|||
parser.add_argument("--notificationSounds", type=str2bool, nargs='?',
|
||||
const=True, default=True,
|
||||
help="Play notification sounds")
|
||||
parser.add_argument("--authenticatedFetch", type=str2bool, nargs='?',
|
||||
parser.add_argument("--secureMode", type=str2bool, nargs='?',
|
||||
const=True, default=False,
|
||||
help="Enable authentication on GET requests" +
|
||||
" for json (authenticated fetch)")
|
||||
help="Requires all GET requests to be signed, " +
|
||||
"so that the sender can be identifies and " +
|
||||
"blocked if neccessary")
|
||||
parser.add_argument("--instanceOnlySkillsSearch", type=str2bool, nargs='?',
|
||||
const=True, default=False,
|
||||
help="Skills searches only return " +
|
||||
|
|
@ -633,12 +643,13 @@ if args.tests:
|
|||
sys.exit()
|
||||
if args.testsnetwork:
|
||||
print('Network Tests')
|
||||
testSharedItemsFederation()
|
||||
testGroupFollow()
|
||||
testPostMessageBetweenServers()
|
||||
testFollowBetweenServers()
|
||||
testClientToServer()
|
||||
testUpdateActor()
|
||||
baseDir = os.getcwd()
|
||||
testSharedItemsFederation(baseDir)
|
||||
testGroupFollow(baseDir)
|
||||
testPostMessageBetweenServers(baseDir)
|
||||
testFollowBetweenServers(baseDir)
|
||||
testClientToServer(baseDir)
|
||||
testUpdateActor(baseDir)
|
||||
print('All tests succeeded')
|
||||
sys.exit()
|
||||
|
||||
|
|
@ -662,6 +673,12 @@ if args.libretranslateApiKey:
|
|||
setConfigParam(baseDir, 'libretranslateApiKey', args.libretranslateApiKey)
|
||||
|
||||
if args.posts:
|
||||
if not args.domain:
|
||||
originDomain = getConfigParam(baseDir, 'domain')
|
||||
else:
|
||||
originDomain = args.domain
|
||||
if debug:
|
||||
print('originDomain: ' + str(originDomain))
|
||||
if '@' not in args.posts:
|
||||
if '/users/' in args.posts:
|
||||
postsNickname = getNicknameFromActor(args.posts)
|
||||
|
|
@ -688,9 +705,11 @@ if args.posts:
|
|||
proxyType = 'gnunet'
|
||||
if not args.language:
|
||||
args.language = 'en'
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, originDomain)
|
||||
getPublicPostsOfPerson(baseDir, nickname, domain, False, True,
|
||||
proxyType, args.port, httpPrefix, debug,
|
||||
__version__, args.language)
|
||||
__version__, args.language,
|
||||
signingPrivateKeyPem, originDomain)
|
||||
sys.exit()
|
||||
|
||||
if args.postDomains:
|
||||
|
|
@ -722,13 +741,22 @@ if args.postDomains:
|
|||
domainList = []
|
||||
if not args.language:
|
||||
args.language = 'en'
|
||||
signingPrivateKeyPem = None
|
||||
if not args.domain:
|
||||
originDomain = getConfigParam(baseDir, 'domain')
|
||||
else:
|
||||
originDomain = args.domain
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, originDomain)
|
||||
domainList = getPublicPostDomains(None,
|
||||
baseDir, nickname, domain,
|
||||
originDomain,
|
||||
proxyType, args.port,
|
||||
httpPrefix, debug,
|
||||
__version__,
|
||||
wordFrequency, domainList,
|
||||
args.language)
|
||||
args.language,
|
||||
signingPrivateKeyPem)
|
||||
for postDomain in domainList:
|
||||
print(postDomain)
|
||||
sys.exit()
|
||||
|
|
@ -765,13 +793,17 @@ if args.postDomainsBlocked:
|
|||
domainList = []
|
||||
if not args.language:
|
||||
args.language = 'en'
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
domainList = getPublicPostDomainsBlocked(None,
|
||||
baseDir, nickname, domain,
|
||||
proxyType, args.port,
|
||||
httpPrefix, debug,
|
||||
__version__,
|
||||
wordFrequency, domainList,
|
||||
args.language)
|
||||
args.language,
|
||||
signingPrivateKeyPem)
|
||||
for postDomain in domainList:
|
||||
print(postDomain)
|
||||
sys.exit()
|
||||
|
|
@ -806,12 +838,16 @@ if args.checkDomains:
|
|||
maxBlockedDomains = 0
|
||||
if not args.language:
|
||||
args.language = 'en'
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
checkDomains(None,
|
||||
baseDir, nickname, domain,
|
||||
proxyType, args.port,
|
||||
httpPrefix, debug,
|
||||
__version__,
|
||||
maxBlockedDomains, False, args.language)
|
||||
maxBlockedDomains, False, args.language,
|
||||
signingPrivateKeyPem)
|
||||
sys.exit()
|
||||
|
||||
if args.socnet:
|
||||
|
|
@ -825,10 +861,19 @@ if args.socnet:
|
|||
proxyType = 'tor'
|
||||
if not args.language:
|
||||
args.language = 'en'
|
||||
if not args.domain:
|
||||
args.domain = getConfigParam(baseDir, 'domain')
|
||||
domain = ''
|
||||
if args.domain:
|
||||
domain = args.domain
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
dotGraph = instancesGraph(baseDir, args.socnet,
|
||||
proxyType, args.port,
|
||||
httpPrefix, debug,
|
||||
__version__, args.language)
|
||||
__version__, args.language,
|
||||
signingPrivateKeyPem)
|
||||
try:
|
||||
with open('socnet.dot', 'w+') as fp:
|
||||
fp.write(dotGraph)
|
||||
|
|
@ -838,6 +883,12 @@ if args.socnet:
|
|||
sys.exit()
|
||||
|
||||
if args.postsraw:
|
||||
if not args.domain:
|
||||
originDomain = getConfigParam(baseDir, 'domain')
|
||||
else:
|
||||
originDomain = args.domain
|
||||
if debug:
|
||||
print('originDomain: ' + str(originDomain))
|
||||
if '@' not in args.postsraw:
|
||||
print('Syntax: --postsraw nickname@domain')
|
||||
sys.exit()
|
||||
|
|
@ -854,9 +905,11 @@ if args.postsraw:
|
|||
proxyType = 'gnunet'
|
||||
if not args.language:
|
||||
args.language = 'en'
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, originDomain)
|
||||
getPublicPostsOfPerson(baseDir, nickname, domain, False, False,
|
||||
proxyType, args.port, httpPrefix, debug,
|
||||
__version__, args.language)
|
||||
__version__, args.language,
|
||||
signingPrivateKeyPem, originDomain)
|
||||
sys.exit()
|
||||
|
||||
if args.json:
|
||||
|
|
@ -865,8 +918,20 @@ if args.json:
|
|||
asHeader = {
|
||||
'Accept': 'application/ld+json; profile="' + profileStr + '"'
|
||||
}
|
||||
testJson = getJson(session, args.json, asHeader, None,
|
||||
debug, __version__, httpPrefix, None)
|
||||
if not args.domain:
|
||||
args.domain = getConfigParam(baseDir, 'domain')
|
||||
domain = ''
|
||||
if args.domain:
|
||||
domain = args.domain
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
if debug:
|
||||
print('baseDir: ' + str(baseDir))
|
||||
if signingPrivateKeyPem:
|
||||
print('Obtained instance actor signing key')
|
||||
else:
|
||||
print('Did not obtain instance actor key for ' + domain)
|
||||
testJson = getJson(signingPrivateKeyPem, session, args.json, asHeader,
|
||||
None, debug, __version__, httpPrefix, domain)
|
||||
pprint(testJson)
|
||||
sys.exit()
|
||||
|
||||
|
|
@ -1075,6 +1140,11 @@ if args.approve:
|
|||
postLog = []
|
||||
cachedWebfingers = {}
|
||||
personCache = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
manualApproveFollowRequest(session, baseDir,
|
||||
httpPrefix,
|
||||
args.nickname, domain, port,
|
||||
|
|
@ -1082,7 +1152,8 @@ if args.approve:
|
|||
federationList,
|
||||
sendThreads, postLog,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
sys.exit()
|
||||
|
||||
if args.deny:
|
||||
|
|
@ -1097,6 +1168,11 @@ if args.deny:
|
|||
postLog = []
|
||||
cachedWebfingers = {}
|
||||
personCache = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
manualDenyFollowRequest(session, baseDir,
|
||||
httpPrefix,
|
||||
args.nickname, domain, port,
|
||||
|
|
@ -1104,7 +1180,8 @@ if args.deny:
|
|||
federationList,
|
||||
sendThreads, postLog,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
sys.exit()
|
||||
|
||||
if args.followerspending:
|
||||
|
|
@ -1184,9 +1261,14 @@ if args.message:
|
|||
replyTo = args.replyto
|
||||
followersOnly = False
|
||||
isArticle = False
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending post to ' + args.sendto)
|
||||
|
||||
sendPostViaServer(__version__,
|
||||
sendPostViaServer(signingPrivateKeyPem, __version__,
|
||||
baseDir, session, args.nickname, args.password,
|
||||
domain, port,
|
||||
toNickname, toDomain, toPort, ccUrl,
|
||||
|
|
@ -1216,13 +1298,18 @@ if args.announce:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending announce/repeat of ' + args.announce)
|
||||
|
||||
sendAnnounceViaServer(baseDir, session, args.nickname, args.password,
|
||||
domain, port,
|
||||
httpPrefix, args.announce,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1255,13 +1342,18 @@ if args.box:
|
|||
args.port = 80
|
||||
elif args.gnunet:
|
||||
proxyType = 'gnunet'
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
|
||||
session = createSession(proxyType)
|
||||
boxJson = c2sBoxJson(baseDir, session,
|
||||
args.nickname, args.password,
|
||||
domain, port, httpPrefix,
|
||||
args.box, args.pageNumber,
|
||||
args.debug)
|
||||
args.debug, signingPrivateKeyPem)
|
||||
if boxJson:
|
||||
pprint(boxJson)
|
||||
else:
|
||||
|
|
@ -1311,6 +1403,11 @@ if args.itemName:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending shared item: ' + args.itemName)
|
||||
|
||||
sendShareViaServer(baseDir, session,
|
||||
|
|
@ -1327,7 +1424,8 @@ if args.itemName:
|
|||
args.duration,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__,
|
||||
args.itemPrice, args.itemCurrency)
|
||||
args.itemPrice, args.itemCurrency,
|
||||
signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1348,6 +1446,11 @@ if args.undoItemName:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending undo of shared item: ' + args.undoItemName)
|
||||
|
||||
sendUndoShareViaServer(baseDir, session,
|
||||
|
|
@ -1356,7 +1459,7 @@ if args.undoItemName:
|
|||
httpPrefix,
|
||||
args.undoItemName,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1405,6 +1508,11 @@ if args.wantedItemName:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending wanted item: ' + args.wantedItemName)
|
||||
|
||||
sendWantedViaServer(baseDir, session,
|
||||
|
|
@ -1421,7 +1529,8 @@ if args.wantedItemName:
|
|||
args.duration,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__,
|
||||
args.itemPrice, args.itemCurrency)
|
||||
args.itemPrice, args.itemCurrency,
|
||||
signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1442,6 +1551,11 @@ if args.undoWantedItemName:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending undo of wanted item: ' + args.undoWantedItemName)
|
||||
|
||||
sendUndoWantedViaServer(baseDir, session,
|
||||
|
|
@ -1450,7 +1564,7 @@ if args.undoWantedItemName:
|
|||
httpPrefix,
|
||||
args.undoWantedItemName,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1471,6 +1585,11 @@ if args.like:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending like of ' + args.like)
|
||||
|
||||
sendLikeViaServer(baseDir, session,
|
||||
|
|
@ -1478,7 +1597,7 @@ if args.like:
|
|||
domain, port,
|
||||
httpPrefix, args.like,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1499,6 +1618,11 @@ if args.undolike:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending undo like of ' + args.undolike)
|
||||
|
||||
sendUndoLikeViaServer(baseDir, session,
|
||||
|
|
@ -1506,7 +1630,8 @@ if args.undolike:
|
|||
domain, port,
|
||||
httpPrefix, args.undolike,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__,
|
||||
signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1527,6 +1652,11 @@ if args.bookmark:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending bookmark of ' + args.bookmark)
|
||||
|
||||
sendBookmarkViaServer(baseDir, session,
|
||||
|
|
@ -1534,7 +1664,8 @@ if args.bookmark:
|
|||
domain, port,
|
||||
httpPrefix, args.bookmark,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__,
|
||||
signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1555,6 +1686,11 @@ if args.unbookmark:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending undo bookmark of ' + args.unbookmark)
|
||||
|
||||
sendUndoBookmarkViaServer(baseDir, session,
|
||||
|
|
@ -1562,7 +1698,7 @@ if args.unbookmark:
|
|||
domain, port,
|
||||
httpPrefix, args.unbookmark,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1583,6 +1719,11 @@ if args.delete:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending delete request of ' + args.delete)
|
||||
|
||||
sendDeleteViaServer(baseDir, session,
|
||||
|
|
@ -1590,7 +1731,7 @@ if args.delete:
|
|||
domain, port,
|
||||
httpPrefix, args.delete,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -1623,6 +1764,11 @@ if args.follow:
|
|||
followHttpPrefix = httpPrefix
|
||||
if args.follow.startswith('https'):
|
||||
followHttpPrefix = 'https'
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
|
||||
sendFollowRequestViaServer(baseDir, session,
|
||||
args.nickname, args.password,
|
||||
|
|
@ -1630,7 +1776,7 @@ if args.follow:
|
|||
followNickname, followDomain, followPort,
|
||||
httpPrefix,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__, signingPrivateKeyPem)
|
||||
for t in range(20):
|
||||
time.sleep(1)
|
||||
# TODO some method to know if it worked
|
||||
|
|
@ -1664,6 +1810,11 @@ if args.unfollow:
|
|||
followHttpPrefix = httpPrefix
|
||||
if args.follow.startswith('https'):
|
||||
followHttpPrefix = 'https'
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
|
||||
sendUnfollowRequestViaServer(baseDir, session,
|
||||
args.nickname, args.password,
|
||||
|
|
@ -1671,7 +1822,7 @@ if args.unfollow:
|
|||
followNickname, followDomain, followPort,
|
||||
httpPrefix,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__, signingPrivateKeyPem)
|
||||
for t in range(20):
|
||||
time.sleep(1)
|
||||
# TODO some method to know if it worked
|
||||
|
|
@ -1694,6 +1845,11 @@ if args.followingList:
|
|||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
followHttpPrefix = httpPrefix
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
|
||||
followingJson = \
|
||||
getFollowingViaServer(baseDir, session,
|
||||
|
|
@ -1701,7 +1857,7 @@ if args.followingList:
|
|||
domain, port,
|
||||
httpPrefix, args.pageNumber,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__, signingPrivateKeyPem)
|
||||
if followingJson:
|
||||
pprint(followingJson)
|
||||
sys.exit()
|
||||
|
|
@ -1722,6 +1878,11 @@ if args.followersList:
|
|||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
followHttpPrefix = httpPrefix
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
|
||||
followersJson = \
|
||||
getFollowersViaServer(baseDir, session,
|
||||
|
|
@ -1729,7 +1890,8 @@ if args.followersList:
|
|||
domain, port,
|
||||
httpPrefix, args.pageNumber,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__,
|
||||
signingPrivateKeyPem)
|
||||
if followersJson:
|
||||
pprint(followersJson)
|
||||
sys.exit()
|
||||
|
|
@ -1750,6 +1912,11 @@ if args.followRequestsList:
|
|||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
followHttpPrefix = httpPrefix
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
|
||||
followRequestsJson = \
|
||||
getFollowRequestsViaServer(baseDir, session,
|
||||
|
|
@ -1757,7 +1924,7 @@ if args.followRequestsList:
|
|||
domain, port,
|
||||
httpPrefix, args.pageNumber,
|
||||
cachedWebfingers, personCache,
|
||||
debug, __version__)
|
||||
debug, __version__, signingPrivateKeyPem)
|
||||
if followRequestsJson:
|
||||
pprint(followRequestsJson)
|
||||
sys.exit()
|
||||
|
|
@ -1797,9 +1964,14 @@ if args.migrations:
|
|||
httpPrefix = 'https'
|
||||
port = 443
|
||||
session = createSession(proxyType)
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
ctr = migrateAccounts(baseDir, session,
|
||||
httpPrefix, cachedWebfingers,
|
||||
True)
|
||||
True, signingPrivateKeyPem)
|
||||
if ctr == 0:
|
||||
print('No followed accounts have moved')
|
||||
else:
|
||||
|
|
@ -1807,7 +1979,17 @@ if args.migrations:
|
|||
sys.exit()
|
||||
|
||||
if args.actor:
|
||||
getActorJson(args.domain, args.actor, args.http, args.gnunet, debug)
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
if debug:
|
||||
print('baseDir: ' + str(baseDir))
|
||||
if signingPrivateKeyPem:
|
||||
print('Obtained instance actor signing key')
|
||||
else:
|
||||
print('Did not obtain instance actor key for ' + domain)
|
||||
getActorJson(domain, args.actor, args.http, args.gnunet,
|
||||
debug, False, signingPrivateKeyPem)
|
||||
sys.exit()
|
||||
|
||||
if args.followers:
|
||||
|
|
@ -1882,10 +2064,17 @@ if args.followers:
|
|||
if nickname == 'inbox':
|
||||
nickname = domain
|
||||
|
||||
hostDomain = None
|
||||
if args.domain:
|
||||
hostDomain = args.domain
|
||||
handle = nickname + '@' + domain
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
wfRequest = webfingerHandle(session, handle,
|
||||
httpPrefix, cachedWebfingers,
|
||||
None, __version__, debug, False)
|
||||
hostDomain, __version__, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
print('Unable to webfinger ' + handle)
|
||||
sys.exit()
|
||||
|
|
@ -1927,9 +2116,12 @@ if args.followers:
|
|||
asHeader = {
|
||||
'Accept': 'application/ld+json; profile="' + profileStr + '"'
|
||||
}
|
||||
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
followersList = \
|
||||
downloadFollowCollection('followers', session,
|
||||
downloadFollowCollection(signingPrivateKeyPem,
|
||||
'followers', session,
|
||||
httpPrefix, personUrl, 1, 3)
|
||||
if followersList:
|
||||
for actor in followersList:
|
||||
|
|
@ -2179,6 +2371,11 @@ if args.skill:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending ' + args.skill + ' skill level ' +
|
||||
str(args.skillLevelPercent) + ' for ' + nickname)
|
||||
|
||||
|
|
@ -2188,7 +2385,7 @@ if args.skill:
|
|||
httpPrefix,
|
||||
args.skill, args.skillLevelPercent,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -2209,6 +2406,11 @@ if args.availability:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending availability status of ' + nickname +
|
||||
' as ' + args.availability)
|
||||
|
||||
|
|
@ -2217,7 +2419,7 @@ if args.availability:
|
|||
httpPrefix,
|
||||
args.availability,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -2318,13 +2520,18 @@ if args.block:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending block of ' + args.block)
|
||||
|
||||
sendBlockViaServer(baseDir, session, nickname, args.password,
|
||||
domain, port,
|
||||
httpPrefix, args.block,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -2345,13 +2552,18 @@ if args.mute:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending mute of ' + args.mute)
|
||||
|
||||
sendMuteViaServer(baseDir, session, nickname, args.password,
|
||||
domain, port,
|
||||
httpPrefix, args.mute,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -2372,13 +2584,18 @@ if args.unmute:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending undo mute of ' + args.unmute)
|
||||
|
||||
sendUndoMuteViaServer(baseDir, session, nickname, args.password,
|
||||
domain, port,
|
||||
httpPrefix, args.unmute,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -2411,13 +2628,18 @@ if args.unblock:
|
|||
session = createSession(proxyType)
|
||||
personCache = {}
|
||||
cachedWebfingers = {}
|
||||
if not domain:
|
||||
domain = getConfigParam(baseDir, 'domain')
|
||||
signingPrivateKeyPem = None
|
||||
if args.secureMode:
|
||||
signingPrivateKeyPem = getInstanceActorKey(baseDir, domain)
|
||||
print('Sending undo block of ' + args.unblock)
|
||||
|
||||
sendUndoBlockViaServer(baseDir, session, nickname, args.password,
|
||||
domain, port,
|
||||
httpPrefix, args.unblock,
|
||||
cachedWebfingers, personCache,
|
||||
True, __version__)
|
||||
True, __version__, signingPrivateKeyPem)
|
||||
for i in range(10):
|
||||
# TODO detect send success/fail
|
||||
time.sleep(1)
|
||||
|
|
@ -2803,6 +3025,15 @@ if YTDomain:
|
|||
if '.' in YTDomain:
|
||||
args.YTReplacementDomain = YTDomain
|
||||
|
||||
twitterDomain = getConfigParam(baseDir, 'twitterdomain')
|
||||
if twitterDomain:
|
||||
if '://' in twitterDomain:
|
||||
twitterDomain = twitterDomain.split('://')[1]
|
||||
if '/' in twitterDomain:
|
||||
twitterDomain = twitterDomain.split('/')[0]
|
||||
if '.' in twitterDomain:
|
||||
args.twitterReplacementDomain = twitterDomain
|
||||
|
||||
if setTheme(baseDir, themeName, domain,
|
||||
args.allowLocalNetworkAccess, args.language):
|
||||
print('Theme set to ' + themeName)
|
||||
|
|
@ -2833,7 +3064,8 @@ if args.defaultCurrency:
|
|||
print('Default currency set to ' + args.defaultCurrency)
|
||||
|
||||
if __name__ == "__main__":
|
||||
runDaemon(args.lowBandwidth, args.maxLikeCount,
|
||||
runDaemon(args.defaultReplyIntervalHours,
|
||||
args.lowBandwidth, args.maxLikeCount,
|
||||
sharedItemsFederatedDomains,
|
||||
userAgentsBlocked,
|
||||
args.logLoginFailures,
|
||||
|
|
@ -2869,9 +3101,10 @@ if __name__ == "__main__":
|
|||
instanceId, args.client, baseDir,
|
||||
domain, onionDomain, i2pDomain,
|
||||
args.YTReplacementDomain,
|
||||
args.twitterReplacementDomain,
|
||||
port, proxyPort, httpPrefix,
|
||||
federationList, args.maxMentions,
|
||||
args.maxEmoji, args.authenticatedFetch,
|
||||
args.maxEmoji, args.secureMode,
|
||||
proxyType, args.maxReplies,
|
||||
args.domainMaxPostsPerDay,
|
||||
args.accountMaxPostsPerDay,
|
||||
|
|
|
|||
2
feeds.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "RSS Feeds"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Moderation"
|
||||
|
||||
|
|
|
|||
127
follow.py
|
|
@ -3,14 +3,16 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
from pprint import pprint
|
||||
import os
|
||||
from utils import hasObjectStringObject
|
||||
from utils import hasObjectStringType
|
||||
from utils import hasActor
|
||||
from utils import removeDomainPort
|
||||
from utils import hasObjectDict
|
||||
from utils import hasUsersPath
|
||||
from utils import getFullDomain
|
||||
from utils import isSystemAccount
|
||||
|
|
@ -211,6 +213,12 @@ def isFollowerOfPerson(baseDir: str, nickname: str, domain: str,
|
|||
followerNickname: str, followerDomain: str) -> bool:
|
||||
"""is the given nickname a follower of followerNickname?
|
||||
"""
|
||||
if not followerDomain:
|
||||
print('No followerDomain')
|
||||
return False
|
||||
if not followerNickname:
|
||||
print('No followerNickname for ' + followerDomain)
|
||||
return False
|
||||
domain = removeDomainPort(domain)
|
||||
followersFile = acctDir(baseDir, nickname, domain) + '/followers.txt'
|
||||
if not os.path.isfile(followersFile):
|
||||
|
|
@ -308,7 +316,10 @@ def clearFollows(baseDir: str, nickname: str, domain: str,
|
|||
os.mkdir(baseDir + '/accounts/' + handle)
|
||||
filename = baseDir + '/accounts/' + handle + '/' + followFile
|
||||
if os.path.isfile(filename):
|
||||
os.remove(filename)
|
||||
try:
|
||||
os.remove(filename)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
|
||||
def clearFollowers(baseDir: str, nickname: str, domain: str) -> None:
|
||||
|
|
@ -631,16 +642,15 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
|
|||
cachedWebfingers: {}, personCache: {},
|
||||
messageJson: {}, federationList: [],
|
||||
debug: bool, projectVersion: str,
|
||||
maxFollowers: int, onionDomain: str) -> bool:
|
||||
maxFollowers: int, onionDomain: str,
|
||||
signingPrivateKeyPem: str) -> bool:
|
||||
"""Receives a follow request within the POST section of HTTPServer
|
||||
"""
|
||||
if not messageJson['type'].startswith('Follow'):
|
||||
if not messageJson['type'].startswith('Join'):
|
||||
return False
|
||||
print('Receiving follow request')
|
||||
if not messageJson.get('actor'):
|
||||
if debug:
|
||||
print('DEBUG: follow request has no actor')
|
||||
if not hasActor(messageJson, debug):
|
||||
return False
|
||||
if not hasUsersPath(messageJson['actor']):
|
||||
if debug:
|
||||
|
|
@ -743,7 +753,8 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
|
|||
print('Obtaining the following actor: ' + messageJson['actor'])
|
||||
if not getPersonPubKey(baseDir, session, messageJson['actor'],
|
||||
personCache, debug, projectVersion,
|
||||
httpPrefix, domainToFollow, onionDomain):
|
||||
httpPrefix, domainToFollow, onionDomain,
|
||||
signingPrivateKeyPem):
|
||||
if debug:
|
||||
print('Unable to obtain following actor: ' +
|
||||
messageJson['actor'])
|
||||
|
|
@ -779,7 +790,8 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
|
|||
print('Obtaining the following actor: ' + messageJson['actor'])
|
||||
if not getPersonPubKey(baseDir, session, messageJson['actor'],
|
||||
personCache, debug, projectVersion,
|
||||
httpPrefix, domainToFollow, onionDomain):
|
||||
httpPrefix, domainToFollow, onionDomain,
|
||||
signingPrivateKeyPem):
|
||||
if debug:
|
||||
print('Unable to obtain following actor: ' +
|
||||
messageJson['actor'])
|
||||
|
|
@ -824,7 +836,8 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
|
|||
messageJson['actor'], federationList,
|
||||
messageJson, sendThreads, postLog,
|
||||
cachedWebfingers, personCache,
|
||||
debug, projectVersion, True)
|
||||
debug, projectVersion, True,
|
||||
signingPrivateKeyPem)
|
||||
|
||||
|
||||
def followedAccountAccepts(session, baseDir: str, httpPrefix: str,
|
||||
|
|
@ -835,7 +848,8 @@ def followedAccountAccepts(session, baseDir: str, httpPrefix: str,
|
|||
followJson: {}, sendThreads: [], postLog: [],
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str,
|
||||
removeFollowActivity: bool):
|
||||
removeFollowActivity: bool,
|
||||
signingPrivateKeyPem: str):
|
||||
"""The person receiving a follow request accepts the new follower
|
||||
and sends back an Accept activity
|
||||
"""
|
||||
|
|
@ -884,7 +898,8 @@ def followedAccountAccepts(session, baseDir: str, httpPrefix: str,
|
|||
federationList,
|
||||
sendThreads, postLog, cachedWebfingers,
|
||||
personCache, debug, projectVersion, None,
|
||||
groupAccount)
|
||||
groupAccount, signingPrivateKeyPem,
|
||||
7856837)
|
||||
|
||||
|
||||
def followedAccountRejects(session, baseDir: str, httpPrefix: str,
|
||||
|
|
@ -894,7 +909,8 @@ def followedAccountRejects(session, baseDir: str, httpPrefix: str,
|
|||
federationList: [],
|
||||
sendThreads: [], postLog: [],
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str):
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str):
|
||||
"""The person receiving a follow request rejects the new follower
|
||||
and sends back a Reject activity
|
||||
"""
|
||||
|
|
@ -949,7 +965,8 @@ def followedAccountRejects(session, baseDir: str, httpPrefix: str,
|
|||
federationList,
|
||||
sendThreads, postLog, cachedWebfingers,
|
||||
personCache, debug, projectVersion, None,
|
||||
groupAccount)
|
||||
groupAccount, signingPrivateKeyPem,
|
||||
6393063)
|
||||
|
||||
|
||||
def sendFollowRequest(session, baseDir: str,
|
||||
|
|
@ -960,9 +977,12 @@ def sendFollowRequest(session, baseDir: str,
|
|||
clientToServer: bool, federationList: [],
|
||||
sendThreads: [], postLog: [], cachedWebfingers: {},
|
||||
personCache: {}, debug: bool,
|
||||
projectVersion: str) -> {}:
|
||||
projectVersion: str, signingPrivateKeyPem: str) -> {}:
|
||||
"""Gets the json object for sending a follow request
|
||||
"""
|
||||
if not signingPrivateKeyPem:
|
||||
print('WARN: follow request without signing key')
|
||||
|
||||
if not domainPermitted(followDomain, federationList):
|
||||
print('You are not permitted to follow the domain ' + followDomain)
|
||||
return None
|
||||
|
|
@ -1016,7 +1036,8 @@ def sendFollowRequest(session, baseDir: str,
|
|||
httpPrefix, True, clientToServer,
|
||||
federationList,
|
||||
sendThreads, postLog, cachedWebfingers, personCache,
|
||||
debug, projectVersion, None, groupAccount)
|
||||
debug, projectVersion, None, groupAccount,
|
||||
signingPrivateKeyPem, 8234389)
|
||||
|
||||
return newFollowJson
|
||||
|
||||
|
|
@ -1028,7 +1049,8 @@ def sendFollowRequestViaServer(baseDir: str, session,
|
|||
followPort: int,
|
||||
httpPrefix: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Creates a follow request via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -1057,7 +1079,8 @@ def sendFollowRequestViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = \
|
||||
webfingerHandle(session, handle, httpPrefix, cachedWebfingers,
|
||||
fromDomain, projectVersion, debug, False)
|
||||
fromDomain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: follow request webfinger failed for ' + handle)
|
||||
|
|
@ -1070,11 +1093,13 @@ def sendFollowRequestViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
originDomain = fromDomain
|
||||
(inboxUrl, pubKeyId, pubKey,
|
||||
fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName) = getPersonBox(baseDir, session, wfRequest, personCache,
|
||||
projectVersion, httpPrefix, fromNickname,
|
||||
fromDomain, postToBox, 52025)
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem, originDomain,
|
||||
baseDir, session, wfRequest, personCache,
|
||||
projectVersion, httpPrefix, fromNickname,
|
||||
fromDomain, postToBox, 52025)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -1114,7 +1139,8 @@ def sendUnfollowRequestViaServer(baseDir: str, session,
|
|||
followPort: int,
|
||||
httpPrefix: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Creates a unfollow request via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -1147,7 +1173,8 @@ def sendUnfollowRequestViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = \
|
||||
webfingerHandle(session, handle, httpPrefix, cachedWebfingers,
|
||||
fromDomain, projectVersion, debug, False)
|
||||
fromDomain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: unfollow webfinger failed for ' + handle)
|
||||
|
|
@ -1160,14 +1187,16 @@ def sendUnfollowRequestViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
(inboxUrl, pubKeyId, pubKey,
|
||||
fromPersonId, sharedInbox,
|
||||
avatarUrl, displayName) = getPersonBox(baseDir, session,
|
||||
wfRequest, personCache,
|
||||
projectVersion, httpPrefix,
|
||||
fromNickname,
|
||||
fromDomain, postToBox,
|
||||
76536)
|
||||
originDomain = fromDomain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem,
|
||||
originDomain,
|
||||
baseDir, session,
|
||||
wfRequest, personCache,
|
||||
projectVersion, httpPrefix,
|
||||
fromNickname,
|
||||
fromDomain, postToBox,
|
||||
76536)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -1205,7 +1234,8 @@ def getFollowingViaServer(baseDir: str, session,
|
|||
domain: str, port: int,
|
||||
httpPrefix: str, pageNumber: int,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Gets a page from the following collection as json
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -1227,9 +1257,8 @@ def getFollowingViaServer(baseDir: str, session,
|
|||
pageNumber = 1
|
||||
url = followActor + '/following?page=' + str(pageNumber)
|
||||
followingJson = \
|
||||
getJson(session, url, headers, {}, debug,
|
||||
__version__, httpPrefix,
|
||||
domain, 10, True)
|
||||
getJson(signingPrivateKeyPem, session, url, headers, {}, debug,
|
||||
__version__, httpPrefix, domain, 10, True)
|
||||
if not followingJson:
|
||||
if debug:
|
||||
print('DEBUG: GET following list failed for c2s to ' + url)
|
||||
|
|
@ -1246,7 +1275,8 @@ def getFollowersViaServer(baseDir: str, session,
|
|||
domain: str, port: int,
|
||||
httpPrefix: str, pageNumber: int,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Gets a page from the followers collection as json
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -1268,7 +1298,7 @@ def getFollowersViaServer(baseDir: str, session,
|
|||
pageNumber = 1
|
||||
url = followActor + '/followers?page=' + str(pageNumber)
|
||||
followersJson = \
|
||||
getJson(session, url, headers, {}, debug,
|
||||
getJson(signingPrivateKeyPem, session, url, headers, {}, debug,
|
||||
__version__, httpPrefix, domain, 10, True)
|
||||
if not followersJson:
|
||||
if debug:
|
||||
|
|
@ -1286,7 +1316,8 @@ def getFollowRequestsViaServer(baseDir: str, session,
|
|||
domain: str, port: int,
|
||||
httpPrefix: str, pageNumber: int,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Gets a page from the follow requests collection as json
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -1308,7 +1339,7 @@ def getFollowRequestsViaServer(baseDir: str, session,
|
|||
pageNumber = 1
|
||||
url = followActor + '/followrequests?page=' + str(pageNumber)
|
||||
followersJson = \
|
||||
getJson(session, url, headers, {}, debug,
|
||||
getJson(signingPrivateKeyPem, session, url, headers, {}, debug,
|
||||
__version__, httpPrefix, domain, 10, True)
|
||||
if not followersJson:
|
||||
if debug:
|
||||
|
|
@ -1326,7 +1357,8 @@ def approveFollowRequestViaServer(baseDir: str, session,
|
|||
domain: str, port: int,
|
||||
httpPrefix: str, approveHandle: int,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> str:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> str:
|
||||
"""Approves a follow request
|
||||
This is not exactly via c2s though. It simulates pressing the Approve
|
||||
button on the web interface
|
||||
|
|
@ -1348,7 +1380,7 @@ def approveFollowRequestViaServer(baseDir: str, session,
|
|||
|
||||
url = actor + '/followapprove=' + approveHandle
|
||||
approveHtml = \
|
||||
getJson(session, url, headers, {}, debug,
|
||||
getJson(signingPrivateKeyPem, session, url, headers, {}, debug,
|
||||
__version__, httpPrefix, domain, 10, True)
|
||||
if not approveHtml:
|
||||
if debug:
|
||||
|
|
@ -1366,7 +1398,8 @@ def denyFollowRequestViaServer(baseDir: str, session,
|
|||
domain: str, port: int,
|
||||
httpPrefix: str, denyHandle: int,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> str:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> str:
|
||||
"""Denies a follow request
|
||||
This is not exactly via c2s though. It simulates pressing the Deny
|
||||
button on the web interface
|
||||
|
|
@ -1388,7 +1421,7 @@ def denyFollowRequestViaServer(baseDir: str, session,
|
|||
|
||||
url = actor + '/followdeny=' + denyHandle
|
||||
denyHtml = \
|
||||
getJson(session, url, headers, {}, debug,
|
||||
getJson(signingPrivateKeyPem, session, url, headers, {}, debug,
|
||||
__version__, httpPrefix, domain, 10, True)
|
||||
if not denyHtml:
|
||||
if debug:
|
||||
|
|
@ -1456,19 +1489,15 @@ def outboxUndoFollow(baseDir: str, messageJson: {}, debug: bool) -> None:
|
|||
return
|
||||
if not messageJson['type'] == 'Undo':
|
||||
return
|
||||
if not hasObjectDict(messageJson):
|
||||
return
|
||||
if not messageJson['object'].get('type'):
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if not messageJson['object']['type'] == 'Follow':
|
||||
if not messageJson['object']['type'] == 'Join':
|
||||
return
|
||||
if not messageJson['object'].get('object'):
|
||||
if not hasObjectStringObject(messageJson, debug):
|
||||
return
|
||||
if not messageJson['object'].get('actor'):
|
||||
return
|
||||
if not isinstance(messageJson['object']['object'], str):
|
||||
return
|
||||
if debug:
|
||||
print('DEBUG: undo follow arrived in outbox')
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Calendar"
|
||||
|
||||
|
|
|
|||
|
|
@ -62,8 +62,6 @@ Create a web server configuration:
|
|||
|
||||
And paste the following:
|
||||
|
||||
proxy_cache_path /var/www/cache levels=1:2 keys_zone=my_cache:10m max_size=10g
|
||||
inactive=60m use_temp_path=off;
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
|
@ -115,11 +113,12 @@ And paste the following:
|
|||
try_files $uri =404;
|
||||
}
|
||||
|
||||
keepalive_timeout 70;
|
||||
sendfile on;
|
||||
|
||||
location / {
|
||||
proxy_http_version 1.1;
|
||||
client_max_body_size 31M;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
|
||||
|
|
@ -135,18 +134,14 @@ And paste the following:
|
|||
proxy_redirect off;
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
location ~ ^/accounts/(avatars|headers)/(.*).(png|jpg|gif|webp|svg) {
|
||||
expires 1d;
|
||||
proxy_pass http://localhost:7156;
|
||||
}
|
||||
proxy_pass http://localhost:7156;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
}
|
||||
|
||||
Enable the site:
|
||||
|
||||
ln -s /etc/nginx/sites-available/YOUR_DOMAIN /etc/nginx/sites-enabled/
|
||||
mkdir /var/www/cache
|
||||
|
||||
Forward port 443 from your internet router to your server. If you have dynamic DNS make sure its configured. Add a TLS certificate:
|
||||
|
||||
|
|
|
|||
8
git.py
|
|
@ -3,14 +3,14 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
import os
|
||||
import html
|
||||
from utils import hasObjectDict
|
||||
from utils import acctDir
|
||||
from utils import hasObjectStringType
|
||||
|
||||
|
||||
def _gitFormatContent(content: str) -> str:
|
||||
|
|
@ -115,9 +115,7 @@ def convertPostToPatch(baseDir: str, nickname: str, domain: str,
|
|||
"""Detects whether the given post contains a patch
|
||||
and if so then converts it to a Patch ActivityPub type
|
||||
"""
|
||||
if not hasObjectDict(postJsonObject):
|
||||
return False
|
||||
if not postJsonObject['object'].get('type'):
|
||||
if not hasObjectStringType(postJsonObject, False):
|
||||
return False
|
||||
if postJsonObject['object']['type'] == 'Patch':
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
|
||||
|
|
|
|||
116
httpsig.py
|
|
@ -4,7 +4,7 @@ __credits__ = ['lamia']
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Security"
|
||||
|
||||
|
|
@ -24,6 +24,7 @@ from time import gmtime, strftime
|
|||
import datetime
|
||||
from utils import getFullDomain
|
||||
from utils import getSHA256
|
||||
from utils import getSHA512
|
||||
from utils import localActorUrl
|
||||
|
||||
|
||||
|
|
@ -39,7 +40,8 @@ def signPostHeaders(dateStr: str, privateKeyPem: str,
|
|||
toDomain: str, toPort: int,
|
||||
path: str,
|
||||
httpPrefix: str,
|
||||
messageBodyJsonStr: str) -> str:
|
||||
messageBodyJsonStr: str,
|
||||
contentType: str) -> str:
|
||||
"""Returns a raw signature string that can be plugged into a header and
|
||||
used to verify the authenticity of an HTTP transmission.
|
||||
"""
|
||||
|
|
@ -49,13 +51,18 @@ def signPostHeaders(dateStr: str, privateKeyPem: str,
|
|||
|
||||
if not dateStr:
|
||||
dateStr = strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
|
||||
keyID = localActorUrl(httpPrefix, nickname, domain) + '#main-key'
|
||||
if nickname != domain and nickname.lower() != 'actor':
|
||||
keyID = localActorUrl(httpPrefix, nickname, domain)
|
||||
else:
|
||||
# instance actor
|
||||
keyID = httpPrefix + '://' + domain + '/actor'
|
||||
keyID += '#main-key'
|
||||
if not messageBodyJsonStr:
|
||||
headers = {
|
||||
'(request-target)': f'post {path}',
|
||||
'(request-target)': f'get {path}',
|
||||
'host': toDomain,
|
||||
'date': dateStr,
|
||||
'content-type': 'application/json'
|
||||
'accept': contentType
|
||||
}
|
||||
else:
|
||||
bodyDigest = messageContentDigest(messageBodyJsonStr)
|
||||
|
|
@ -78,7 +85,8 @@ def signPostHeaders(dateStr: str, privateKeyPem: str,
|
|||
signedHeaderText = ''
|
||||
for headerKey in signedHeaderKeys:
|
||||
signedHeaderText += f'{headerKey}: {headers[headerKey]}\n'
|
||||
signedHeaderText = signedHeaderText.strip()
|
||||
# strip the trailing linefeed
|
||||
signedHeaderText = signedHeaderText.rstrip('\n')
|
||||
# signedHeaderText.encode('ascii') matches
|
||||
headerDigest = getSHA256(signedHeaderText.encode('ascii'))
|
||||
# print('headerDigest2: ' + str(headerDigest))
|
||||
|
|
@ -155,11 +163,18 @@ def signPostHeadersNew(dateStr: str, privateKeyPem: str,
|
|||
for headerKey in signedHeaderKeys:
|
||||
signedHeaderText += f'{headerKey}: {headers[headerKey]}\n'
|
||||
signedHeaderText = signedHeaderText.strip()
|
||||
headerDigest = getSHA256(signedHeaderText.encode('ascii'))
|
||||
|
||||
# Sign the digest. Potentially other signing algorithms can be added here.
|
||||
signature = ''
|
||||
if algorithm == 'rsa-sha256':
|
||||
if algorithm == 'rsa-sha512':
|
||||
headerDigest = getSHA512(signedHeaderText.encode('ascii'))
|
||||
rawSignature = key.sign(headerDigest,
|
||||
padding.PKCS1v15(),
|
||||
hazutils.Prehashed(hashes.SHA512()))
|
||||
signature = base64.b64encode(rawSignature).decode('ascii')
|
||||
else:
|
||||
# default sha256
|
||||
headerDigest = getSHA256(signedHeaderText.encode('ascii'))
|
||||
rawSignature = key.sign(headerDigest,
|
||||
padding.PKCS1v15(),
|
||||
hazutils.Prehashed(hashes.SHA256()))
|
||||
|
|
@ -184,27 +199,35 @@ def signPostHeadersNew(dateStr: str, privateKeyPem: str,
|
|||
return signatureIndexHeader, signatureHeader
|
||||
|
||||
|
||||
def createSignedHeader(privateKeyPem: str, nickname: str,
|
||||
def createSignedHeader(dateStr: str, privateKeyPem: str, nickname: str,
|
||||
domain: str, port: int,
|
||||
toDomain: str, toPort: int,
|
||||
path: str, httpPrefix: str, withDigest: bool,
|
||||
messageBodyJsonStr: str) -> {}:
|
||||
messageBodyJsonStr: str,
|
||||
contentType: str) -> {}:
|
||||
"""Note that the domain is the destination, not the sender
|
||||
"""
|
||||
contentType = 'application/activity+json'
|
||||
headerDomain = getFullDomain(toDomain, toPort)
|
||||
|
||||
dateStr = strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
|
||||
# if no date is given then create one
|
||||
if not dateStr:
|
||||
dateStr = strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
|
||||
|
||||
# Content-Type or Accept header
|
||||
if not contentType:
|
||||
contentType = 'application/activity+json'
|
||||
|
||||
if not withDigest:
|
||||
headers = {
|
||||
'(request-target)': f'post {path}',
|
||||
'(request-target)': f'get {path}',
|
||||
'host': headerDomain,
|
||||
'date': dateStr
|
||||
'date': dateStr,
|
||||
'accept': contentType
|
||||
}
|
||||
signatureHeader = \
|
||||
signPostHeaders(dateStr, privateKeyPem, nickname,
|
||||
domain, port, toDomain, toPort,
|
||||
path, httpPrefix, None)
|
||||
path, httpPrefix, None, contentType)
|
||||
else:
|
||||
bodyDigest = messageContentDigest(messageBodyJsonStr)
|
||||
contentLength = len(messageBodyJsonStr)
|
||||
|
|
@ -220,7 +243,8 @@ def createSignedHeader(privateKeyPem: str, nickname: str,
|
|||
signPostHeaders(dateStr, privateKeyPem, nickname,
|
||||
domain, port,
|
||||
toDomain, toPort,
|
||||
path, httpPrefix, messageBodyJsonStr)
|
||||
path, httpPrefix, messageBodyJsonStr,
|
||||
contentType)
|
||||
headers['signature'] = signatureHeader
|
||||
return headers
|
||||
|
||||
|
|
@ -302,9 +326,13 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
|||
for k, v in [i.split('=', 1) for i in signatureHeader.split(',')]
|
||||
}
|
||||
|
||||
if debug:
|
||||
print('signatureDict: ' + str(signatureDict))
|
||||
|
||||
# Unpack the signed headers and set values based on current headers and
|
||||
# body (if a digest was included)
|
||||
signedHeaderList = []
|
||||
algorithm = 'rsa-sha256'
|
||||
for signedHeader in signatureDict[requestTargetKey].split(fieldSep2):
|
||||
signedHeader = signedHeader.strip()
|
||||
if debug:
|
||||
|
|
@ -323,6 +351,9 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
|||
# if ')' in appendStr:
|
||||
# appendStr = appendStr.split(')')[0]
|
||||
signedHeaderList.append(appendStr)
|
||||
elif signedHeader == 'algorithm':
|
||||
if headers.get(signedHeader):
|
||||
algorithm = headers[signedHeader]
|
||||
elif signedHeader == 'digest':
|
||||
if messageBodyDigest:
|
||||
bodyDigest = messageBodyDigest
|
||||
|
|
@ -333,19 +364,17 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
|||
if headers.get(signedHeader):
|
||||
appendStr = f'content-length: {headers[signedHeader]}'
|
||||
signedHeaderList.append(appendStr)
|
||||
elif headers.get('Content-Length'):
|
||||
contentLength = headers['Content-Length']
|
||||
signedHeaderList.append(f'content-length: {contentLength}')
|
||||
elif headers.get('Content-length'):
|
||||
contentLength = headers['Content-length']
|
||||
appendStr = f'content-length: {contentLength}'
|
||||
signedHeaderList.append(appendStr)
|
||||
else:
|
||||
if headers.get('Content-Length'):
|
||||
contentLength = headers['Content-Length']
|
||||
signedHeaderList.append(f'content-length: {contentLength}')
|
||||
else:
|
||||
if headers.get('Content-length'):
|
||||
contentLength = headers['Content-length']
|
||||
appendStr = f'content-length: {contentLength}'
|
||||
signedHeaderList.append(appendStr)
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: verifyPostHeaders ' + signedHeader +
|
||||
' not found in ' + str(headers))
|
||||
if debug:
|
||||
print('DEBUG: verifyPostHeaders ' + signedHeader +
|
||||
' not found in ' + str(headers))
|
||||
else:
|
||||
if headers.get(signedHeader):
|
||||
if signedHeader == 'date' and not noRecencyCheck:
|
||||
|
|
@ -395,11 +424,10 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
|||
signedHeaderList.append(
|
||||
f'{signedHeader}: {headers[signedHeaderCap]}')
|
||||
|
||||
if debug:
|
||||
print('DEBUG: signedHeaderList: ' + str(signedHeaderList))
|
||||
# Now we have our header data digest
|
||||
signedHeaderText = '\n'.join(signedHeaderList)
|
||||
headerDigest = getSHA256(signedHeaderText.encode('ascii'))
|
||||
if debug:
|
||||
print('signedHeaderText:\n' + signedHeaderText + 'END')
|
||||
|
||||
# Get the signature, verify with public key, return result
|
||||
signature = None
|
||||
|
|
@ -415,15 +443,29 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
|||
else:
|
||||
# Original Mastodon signature
|
||||
signature = base64.b64decode(signatureDict['signature'])
|
||||
if debug:
|
||||
print('signature: ' + algorithm + ' ' +
|
||||
signatureDict['signature'])
|
||||
|
||||
# If extra signing algorithms need to be added then do it here
|
||||
if algorithm == 'rsa-sha256':
|
||||
headerDigest = getSHA256(signedHeaderText.encode('ascii'))
|
||||
paddingStr = padding.PKCS1v15()
|
||||
alg = hazutils.Prehashed(hashes.SHA256())
|
||||
elif algorithm == 'rsa-sha512':
|
||||
headerDigest = getSHA512(signedHeaderText.encode('ascii'))
|
||||
paddingStr = padding.PKCS1v15()
|
||||
alg = hazutils.Prehashed(hashes.SHA512())
|
||||
else:
|
||||
print('Unknown http signature algorithm: ' + algorithm)
|
||||
paddingStr = padding.PKCS1v15()
|
||||
alg = hazutils.Prehashed(hashes.SHA256())
|
||||
headerDigest = ''
|
||||
|
||||
try:
|
||||
pubkey.verify(
|
||||
signature,
|
||||
headerDigest,
|
||||
padding.PKCS1v15(),
|
||||
hazutils.Prehashed(hashes.SHA256()))
|
||||
pubkey.verify(signature, headerDigest, paddingStr, alg)
|
||||
return True
|
||||
except BaseException:
|
||||
if debug:
|
||||
print('DEBUG: verifyPostHeaders pkcs1_15 verify failure')
|
||||
return False
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.1.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
|
||||
import base64, hashlib, sys
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 131 KiB |
2
jami.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
|
||||
|
|
|
|||
177
like.py
|
|
@ -3,10 +3,15 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
import os
|
||||
from pprint import pprint
|
||||
from utils import hasObjectString
|
||||
from utils import hasObjectStringObject
|
||||
from utils import hasObjectStringType
|
||||
from utils import removeDomainPort
|
||||
from utils import hasObjectDict
|
||||
from utils import hasUsersPath
|
||||
|
|
@ -16,10 +21,13 @@ from utils import urlPermitted
|
|||
from utils import getNicknameFromActor
|
||||
from utils import getDomainFromActor
|
||||
from utils import locatePost
|
||||
from utils import updateLikesCollection
|
||||
from utils import undoLikesCollectionEntry
|
||||
from utils import hasGroupType
|
||||
from utils import localActorUrl
|
||||
from utils import loadJson
|
||||
from utils import saveJson
|
||||
from utils import removePostFromCache
|
||||
from utils import getCachedPostFilename
|
||||
from posts import sendSignedJson
|
||||
from session import postJson
|
||||
from webfinger import webfingerHandle
|
||||
|
|
@ -33,7 +41,12 @@ def likedByPerson(postJsonObject: {}, nickname: str, domain: str) -> bool:
|
|||
if noOfLikes(postJsonObject) == 0:
|
||||
return False
|
||||
actorMatch = domain + '/users/' + nickname
|
||||
for item in postJsonObject['object']['likes']['items']:
|
||||
|
||||
obj = postJsonObject
|
||||
if hasObjectDict(postJsonObject):
|
||||
obj = postJsonObject['object']
|
||||
|
||||
for item in obj['likes']['items']:
|
||||
if item['actor'].endswith(actorMatch):
|
||||
return True
|
||||
return False
|
||||
|
|
@ -42,16 +55,17 @@ def likedByPerson(postJsonObject: {}, nickname: str, domain: str) -> bool:
|
|||
def noOfLikes(postJsonObject: {}) -> int:
|
||||
"""Returns the number of likes ona given post
|
||||
"""
|
||||
if not hasObjectDict(postJsonObject):
|
||||
obj = postJsonObject
|
||||
if hasObjectDict(postJsonObject):
|
||||
obj = postJsonObject['object']
|
||||
if not obj.get('likes'):
|
||||
return 0
|
||||
if not postJsonObject['object'].get('likes'):
|
||||
if not isinstance(obj['likes'], dict):
|
||||
return 0
|
||||
if not isinstance(postJsonObject['object']['likes'], dict):
|
||||
return 0
|
||||
if not postJsonObject['object']['likes'].get('items'):
|
||||
postJsonObject['object']['likes']['items'] = []
|
||||
postJsonObject['object']['likes']['totalItems'] = 0
|
||||
return len(postJsonObject['object']['likes']['items'])
|
||||
if not obj['likes'].get('items'):
|
||||
obj['likes']['items'] = []
|
||||
obj['likes']['totalItems'] = 0
|
||||
return len(obj['likes']['items'])
|
||||
|
||||
|
||||
def _like(recentPostsCache: {},
|
||||
|
|
@ -62,7 +76,8 @@ def _like(recentPostsCache: {},
|
|||
clientToServer: bool,
|
||||
sendThreads: [], postLog: [],
|
||||
personCache: {}, cachedWebfingers: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Creates a like
|
||||
actor is the person doing the liking
|
||||
'to' might be a specific person (actor) whose post was liked
|
||||
|
|
@ -122,7 +137,8 @@ def _like(recentPostsCache: {},
|
|||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
httpPrefix, True, clientToServer, federationList,
|
||||
sendThreads, postLog, cachedWebfingers, personCache,
|
||||
debug, projectVersion, None, groupAccount)
|
||||
debug, projectVersion, None, groupAccount,
|
||||
signingPrivateKeyPem, 7367374)
|
||||
|
||||
return newLikeJson
|
||||
|
||||
|
|
@ -135,7 +151,8 @@ def likePost(recentPostsCache: {},
|
|||
likeStatusNumber: int, clientToServer: bool,
|
||||
sendThreads: [], postLog: [],
|
||||
personCache: {}, cachedWebfingers: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Likes a given status post. This is only used by unit tests
|
||||
"""
|
||||
likeDomain = getFullDomain(likeDomain, likePort)
|
||||
|
|
@ -147,7 +164,7 @@ def likePost(recentPostsCache: {},
|
|||
session, baseDir, federationList, nickname, domain, port,
|
||||
ccList, httpPrefix, objectUrl, actorLiked, clientToServer,
|
||||
sendThreads, postLog, personCache, cachedWebfingers,
|
||||
debug, projectVersion)
|
||||
debug, projectVersion, signingPrivateKeyPem)
|
||||
|
||||
|
||||
def sendLikeViaServer(baseDir: str, session,
|
||||
|
|
@ -155,7 +172,8 @@ def sendLikeViaServer(baseDir: str, session,
|
|||
fromDomain: str, fromPort: int,
|
||||
httpPrefix: str, likeUrl: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Creates a like via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -178,7 +196,8 @@ def sendLikeViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = webfingerHandle(session, handle, httpPrefix,
|
||||
cachedWebfingers,
|
||||
fromDomain, projectVersion, debug, False)
|
||||
fromDomain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: like webfinger failed for ' + handle)
|
||||
|
|
@ -191,12 +210,15 @@ def sendLikeViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox,
|
||||
avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
fromNickname, fromDomain,
|
||||
postToBox, 72873)
|
||||
originDomain = fromDomain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem,
|
||||
originDomain,
|
||||
baseDir, session, wfRequest,
|
||||
personCache,
|
||||
projectVersion, httpPrefix,
|
||||
fromNickname, fromDomain,
|
||||
postToBox, 72873)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -233,7 +255,8 @@ def sendUndoLikeViaServer(baseDir: str, session,
|
|||
fromDomain: str, fromPort: int,
|
||||
httpPrefix: str, likeUrl: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
debug: bool, projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
"""Undo a like via c2s
|
||||
"""
|
||||
if not session:
|
||||
|
|
@ -260,7 +283,8 @@ def sendUndoLikeViaServer(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = webfingerHandle(session, handle, httpPrefix,
|
||||
cachedWebfingers,
|
||||
fromDomain, projectVersion, debug, False)
|
||||
fromDomain, projectVersion, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: unlike webfinger failed for ' + handle)
|
||||
|
|
@ -274,12 +298,15 @@ def sendUndoLikeViaServer(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox,
|
||||
avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest,
|
||||
personCache, projectVersion,
|
||||
httpPrefix, fromNickname,
|
||||
fromDomain, postToBox,
|
||||
72625)
|
||||
originDomain = fromDomain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem,
|
||||
originDomain,
|
||||
baseDir, session, wfRequest,
|
||||
personCache, projectVersion,
|
||||
httpPrefix, fromNickname,
|
||||
fromDomain, postToBox,
|
||||
72625)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
@ -325,13 +352,7 @@ def outboxLike(recentPostsCache: {},
|
|||
if debug:
|
||||
print('DEBUG: not a like')
|
||||
return
|
||||
if not messageJson.get('object'):
|
||||
if debug:
|
||||
print('DEBUG: no object in like')
|
||||
return
|
||||
if not isinstance(messageJson['object'], str):
|
||||
if debug:
|
||||
print('DEBUG: like object is not string')
|
||||
if not hasObjectString(messageJson, debug):
|
||||
return
|
||||
if debug:
|
||||
print('DEBUG: c2s like request arrived in outbox')
|
||||
|
|
@ -362,25 +383,13 @@ def outboxUndoLike(recentPostsCache: {},
|
|||
return
|
||||
if not messageJson['type'] == 'Undo':
|
||||
return
|
||||
if not hasObjectDict(messageJson):
|
||||
if debug:
|
||||
print('DEBUG: undo like object is not dict')
|
||||
return
|
||||
if not messageJson['object'].get('type'):
|
||||
if debug:
|
||||
print('DEBUG: undo like - no type')
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if not messageJson['object']['type'] == 'Like':
|
||||
if debug:
|
||||
print('DEBUG: not a undo like')
|
||||
return
|
||||
if not messageJson['object'].get('object'):
|
||||
if debug:
|
||||
print('DEBUG: no object in undo like')
|
||||
return
|
||||
if not isinstance(messageJson['object']['object'], str):
|
||||
if debug:
|
||||
print('DEBUG: undo like object is not string')
|
||||
if not hasObjectStringObject(messageJson, debug):
|
||||
return
|
||||
if debug:
|
||||
print('DEBUG: c2s undo like request arrived in outbox')
|
||||
|
|
@ -398,3 +407,67 @@ def outboxUndoLike(recentPostsCache: {},
|
|||
domain, debug)
|
||||
if debug:
|
||||
print('DEBUG: post undo liked via c2s - ' + postFilename)
|
||||
|
||||
|
||||
def updateLikesCollection(recentPostsCache: {},
|
||||
baseDir: str, postFilename: str,
|
||||
objectUrl: str, actor: str,
|
||||
nickname: str, domain: str, debug: bool) -> None:
|
||||
"""Updates the likes collection within a post
|
||||
"""
|
||||
postJsonObject = loadJson(postFilename)
|
||||
if not postJsonObject:
|
||||
return
|
||||
|
||||
# remove any cached version of this post so that the
|
||||
# like icon is changed
|
||||
removePostFromCache(postJsonObject, recentPostsCache)
|
||||
cachedPostFilename = getCachedPostFilename(baseDir, nickname,
|
||||
domain, postJsonObject)
|
||||
if cachedPostFilename:
|
||||
if os.path.isfile(cachedPostFilename):
|
||||
try:
|
||||
os.remove(cachedPostFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
obj = postJsonObject
|
||||
if hasObjectDict(postJsonObject):
|
||||
obj = postJsonObject['object']
|
||||
|
||||
if not objectUrl.endswith('/likes'):
|
||||
objectUrl = objectUrl + '/likes'
|
||||
if not obj.get('likes'):
|
||||
if debug:
|
||||
print('DEBUG: Adding initial like to ' + objectUrl)
|
||||
likesJson = {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
'id': objectUrl,
|
||||
'type': 'Collection',
|
||||
"totalItems": 1,
|
||||
'items': [{
|
||||
'type': 'Like',
|
||||
'actor': actor
|
||||
}]
|
||||
}
|
||||
obj['likes'] = likesJson
|
||||
else:
|
||||
if not obj['likes'].get('items'):
|
||||
obj['likes']['items'] = []
|
||||
for likeItem in obj['likes']['items']:
|
||||
if likeItem.get('actor'):
|
||||
if likeItem['actor'] == actor:
|
||||
# already liked
|
||||
return
|
||||
newLike = {
|
||||
'type': 'Like',
|
||||
'actor': actor
|
||||
}
|
||||
obj['likes']['items'].append(newLike)
|
||||
itlen = len(obj['likes']['items'])
|
||||
obj['likes']['totalItems'] = itlen
|
||||
|
||||
if debug:
|
||||
print('DEBUG: saving post with likes added')
|
||||
pprint(postJsonObject)
|
||||
saveJson(postJsonObject, postFilename)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ __credits__ = ['Based on ' +
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Security"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
|
|
@ -26,7 +26,8 @@ def manualDenyFollowRequest(session, baseDir: str,
|
|||
sendThreads: [], postLog: [],
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool,
|
||||
projectVersion: str) -> None:
|
||||
projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> None:
|
||||
"""Manually deny a follow request
|
||||
"""
|
||||
accountsDir = acctDir(baseDir, nickname, domain)
|
||||
|
|
@ -60,7 +61,8 @@ def manualDenyFollowRequest(session, baseDir: str,
|
|||
federationList,
|
||||
sendThreads, postLog,
|
||||
cachedWebfingers, personCache,
|
||||
debug, projectVersion)
|
||||
debug, projectVersion,
|
||||
signingPrivateKeyPem)
|
||||
|
||||
print('Follow request from ' + denyHandle + ' was denied.')
|
||||
|
||||
|
|
@ -87,7 +89,8 @@ def manualApproveFollowRequest(session, baseDir: str,
|
|||
sendThreads: [], postLog: [],
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool,
|
||||
projectVersion: str) -> None:
|
||||
projectVersion: str,
|
||||
signingPrivateKeyPem: str) -> None:
|
||||
"""Manually approve a follow request
|
||||
"""
|
||||
handle = nickname + '@' + domain
|
||||
|
|
@ -176,7 +179,8 @@ def manualApproveFollowRequest(session, baseDir: str,
|
|||
cachedWebfingers,
|
||||
personCache,
|
||||
debug,
|
||||
projectVersion, False)
|
||||
projectVersion, False,
|
||||
signingPrivateKeyPem)
|
||||
updateApprovedFollowers = True
|
||||
else:
|
||||
# this isn't the approved follow so it will remain
|
||||
|
|
@ -218,6 +222,12 @@ def manualApproveFollowRequest(session, baseDir: str,
|
|||
# remove the .follow file
|
||||
if followActivityfilename:
|
||||
if os.path.isfile(followActivityfilename):
|
||||
os.remove(followActivityfilename)
|
||||
try:
|
||||
os.remove(followActivityfilename)
|
||||
except BaseException:
|
||||
pass
|
||||
else:
|
||||
os.remove(approveFollowsFilename + '.new')
|
||||
try:
|
||||
os.remove(approveFollowsFilename + '.new')
|
||||
except BaseException:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Web Interface"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "API"
|
||||
|
||||
|
|
@ -145,31 +145,33 @@ def mastoApiV1Response(path: str, callingDomain: str,
|
|||
|
||||
if path.startswith('/api/v1/blocks'):
|
||||
sendJson = []
|
||||
sendJsonStr = 'masto API instance blocks sent'
|
||||
sendJsonStr = 'masto API instance blocks sent ' + path
|
||||
elif path.startswith('/api/v1/favorites'):
|
||||
sendJson = []
|
||||
sendJsonStr = 'masto API favorites sent'
|
||||
sendJsonStr = 'masto API favorites sent ' + path
|
||||
elif path.startswith('/api/v1/follow_requests'):
|
||||
sendJson = []
|
||||
sendJsonStr = 'masto API follow requests sent'
|
||||
sendJsonStr = 'masto API follow requests sent ' + path
|
||||
elif path.startswith('/api/v1/mutes'):
|
||||
sendJson = []
|
||||
sendJsonStr = 'masto API mutes sent'
|
||||
sendJsonStr = 'masto API mutes sent ' + path
|
||||
elif path.startswith('/api/v1/notifications'):
|
||||
sendJson = []
|
||||
sendJsonStr = 'masto API notifications sent'
|
||||
sendJsonStr = 'masto API notifications sent ' + path
|
||||
elif path.startswith('/api/v1/reports'):
|
||||
sendJson = []
|
||||
sendJsonStr = 'masto API reports sent'
|
||||
sendJsonStr = 'masto API reports sent ' + path
|
||||
elif path.startswith('/api/v1/statuses'):
|
||||
sendJson = []
|
||||
sendJsonStr = 'masto API statuses sent'
|
||||
sendJsonStr = 'masto API statuses sent ' + path
|
||||
elif path.startswith('/api/v1/timelines'):
|
||||
sendJson = []
|
||||
sendJsonStr = 'masto API timelines sent'
|
||||
sendJson = {
|
||||
'error': 'This method requires an authenticated user'
|
||||
}
|
||||
sendJsonStr = 'masto API timelines sent ' + path
|
||||
elif path.startswith('/api/v1/custom_emojis'):
|
||||
sendJson = customEmoji
|
||||
sendJsonStr = 'masto API custom emojis sent'
|
||||
sendJsonStr = 'masto API custom emojis sent ' + path
|
||||
|
||||
adminNickname = getConfigParam(baseDir, 'admin')
|
||||
if adminNickname and path == '/api/v1/instance':
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
|
|
|
|||
60
media.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Timeline"
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ import os
|
|||
import time
|
||||
import datetime
|
||||
import subprocess
|
||||
import random
|
||||
from random import randint
|
||||
from hashlib import sha1
|
||||
from auth import createPassword
|
||||
|
|
@ -28,10 +29,33 @@ from shutil import move
|
|||
from city import spoofGeolocation
|
||||
|
||||
|
||||
def replaceYouTube(postJsonObject: {}, replacementDomain: str,
|
||||
systemLanguage: str) -> None:
|
||||
"""Replace YouTube with a replacement domain
|
||||
This denies Google some, but not all, tracking data
|
||||
def _getBlurHash() -> str:
|
||||
"""You may laugh, but this is a lot less computationally intensive,
|
||||
especially on large images, while still providing some visual variety
|
||||
in the timeline
|
||||
"""
|
||||
hashes = [
|
||||
"UfGuaW01%gRi%MM{azofozo0V@xuozn#ofs.",
|
||||
"UFD]o8-;9FIU~qD%j[%M-;j[ofWB?bt7IURj",
|
||||
"UyO|v_1#im=s%y#U%OxDwRt3W9R-ogjHj[WX",
|
||||
"U96vAQt6H;WBt7ofWBa#MbWBo#j[byaze-oe",
|
||||
"UJKA.q01M|IV%LM|RjNGIVj[f6oLjrofaeof",
|
||||
"U9MPjn]?~Cxut~.PS1%1xXIo0fEer_$*^jxG",
|
||||
"UtLENXWCRjju~qayaeaz00j[ofayIVkCkCfQ",
|
||||
"UHGbeg-pbzWZ.ANI$wsQ$H-;E9W?0Nx]?FjE",
|
||||
"UcHU%#4n_ND%?bxatRWBIU%MazxtNaRjs:of",
|
||||
"ULR:TsWr~6xZofWWf6s-~6oK9eR,oes-WXNJ",
|
||||
"U77VQB-:MaMx%L%MogRkMwkCxuoIS*WYjEsl",
|
||||
"U%Nm{8R+%MxuE1t6WBNG-=RjoIt6~Vj]RkR*",
|
||||
"UCM7u;?boft7oft7ayj[~qt7WBoft7oft7Rj"
|
||||
]
|
||||
return random.choice(hashes)
|
||||
|
||||
|
||||
def _replaceSiloDomain(postJsonObject: {},
|
||||
siloDomain: str, replacementDomain: str,
|
||||
systemLanguage: str) -> None:
|
||||
"""Replace a silo domain with a replacement domain
|
||||
"""
|
||||
if not replacementDomain:
|
||||
return
|
||||
|
|
@ -40,14 +64,32 @@ def replaceYouTube(postJsonObject: {}, replacementDomain: str,
|
|||
if not postJsonObject['object'].get('content'):
|
||||
return
|
||||
contentStr = getBaseContentFromPost(postJsonObject, systemLanguage)
|
||||
if 'www.youtube.com' not in contentStr:
|
||||
if siloDomain not in contentStr:
|
||||
return
|
||||
contentStr = contentStr.replace('www.youtube.com', replacementDomain)
|
||||
contentStr = contentStr.replace(siloDomain, replacementDomain)
|
||||
postJsonObject['object']['content'] = contentStr
|
||||
if postJsonObject['object'].get('contentMap'):
|
||||
postJsonObject['object']['contentMap'][systemLanguage] = contentStr
|
||||
|
||||
|
||||
def replaceYouTube(postJsonObject: {}, replacementDomain: str,
|
||||
systemLanguage: str) -> None:
|
||||
"""Replace YouTube with a replacement domain
|
||||
This denies Google some, but not all, tracking data
|
||||
"""
|
||||
_replaceSiloDomain(postJsonObject, 'www.youtube.com',
|
||||
replacementDomain, systemLanguage)
|
||||
|
||||
|
||||
def replaceTwitter(postJsonObject: {}, replacementDomain: str,
|
||||
systemLanguage: str) -> None:
|
||||
"""Replace Twitter with a replacement domain
|
||||
This allows you to view twitter posts without having a twitter account
|
||||
"""
|
||||
_replaceSiloDomain(postJsonObject, 'twitter.com',
|
||||
replacementDomain, systemLanguage)
|
||||
|
||||
|
||||
def _removeMetaData(imageFilename: str, outputFilename: str) -> None:
|
||||
"""Attempts to do this with pure python didn't work well,
|
||||
so better to use a dedicated tool if one is installed
|
||||
|
|
@ -283,6 +325,8 @@ def attachMedia(baseDir: str, httpPrefix: str,
|
|||
createMediaDirs(baseDir, mPath)
|
||||
mediaFilename = baseDir + '/' + mediaPath
|
||||
|
||||
mediaPath = \
|
||||
mediaPath.replace('media/', 'system/media_attachments/files/', 1)
|
||||
attachmentJson = {
|
||||
'mediaType': mediaType,
|
||||
'name': description,
|
||||
|
|
@ -290,7 +334,7 @@ def attachMedia(baseDir: str, httpPrefix: str,
|
|||
'url': httpPrefix + '://' + domain + '/' + mediaPath
|
||||
}
|
||||
if mediaType.startswith('image/'):
|
||||
attachmentJson['focialPoint'] = [0.0, 0.0]
|
||||
attachmentJson['blurhash'] = _getBlurHash()
|
||||
# find the dimensions of the image and add them as metadata
|
||||
attachImageWidth, attachImageHeight = \
|
||||
getImageDimensions(imageFilename)
|
||||
|
|
|
|||
71
metadata.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Metadata"
|
||||
|
||||
|
|
@ -101,8 +101,30 @@ def metaDataInstance(showAccounts: bool,
|
|||
print('WARN: json load exception metaDataInstance')
|
||||
return {}
|
||||
|
||||
rulesList = []
|
||||
rulesFilename = \
|
||||
baseDir + '/accounts/tos.md'
|
||||
if os.path.isfile(rulesFilename):
|
||||
with open(rulesFilename, 'r') as fp:
|
||||
rulesLines = fp.readlines()
|
||||
ruleCtr = 1
|
||||
for line in rulesLines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
rulesList.append({
|
||||
'id': str(ruleCtr),
|
||||
'text': line
|
||||
})
|
||||
ruleCtr += 1
|
||||
|
||||
isBot = False
|
||||
if adminActor['type'] != 'Person':
|
||||
isGroup = False
|
||||
if adminActor['type'] == 'Group':
|
||||
isGroup = True
|
||||
elif adminActor['type'] != 'Person':
|
||||
isBot = True
|
||||
|
||||
url = \
|
||||
|
|
@ -116,16 +138,25 @@ def metaDataInstance(showAccounts: bool,
|
|||
activeAccounts = 1
|
||||
localPosts = 1
|
||||
|
||||
createdAt = ''
|
||||
if adminActor.get('published'):
|
||||
createdAt = adminActor['published']
|
||||
|
||||
instance = {
|
||||
'approval_required': False,
|
||||
'invites_enabled': False,
|
||||
'registrations': registration,
|
||||
'contact_account': {
|
||||
'acct': adminActor['preferredUsername'],
|
||||
'created_at': createdAt,
|
||||
'avatar': adminActor['icon']['url'],
|
||||
'avatar_static': adminActor['icon']['url'],
|
||||
'bot': isBot,
|
||||
'display_name': adminActor['name'],
|
||||
'header': adminActor['image']['url'],
|
||||
'header_static': adminActor['image']['url'],
|
||||
'bot': isBot,
|
||||
'discoverable': True,
|
||||
'group': isGroup,
|
||||
'display_name': adminActor['name'],
|
||||
'locked': adminActor['manuallyApprovesFollowers'],
|
||||
'note': '<p>Admin of ' + domain + '</p>',
|
||||
'url': url,
|
||||
|
|
@ -133,10 +164,9 @@ def metaDataInstance(showAccounts: bool,
|
|||
},
|
||||
'description': instanceDescription,
|
||||
'languages': [systemLanguage],
|
||||
'registrations': registration,
|
||||
'short_description': instanceDescriptionShort,
|
||||
'stats': {
|
||||
'domain_count': 1,
|
||||
'domain_count': 2,
|
||||
'status_count': localPosts,
|
||||
'user_count': activeAccounts
|
||||
},
|
||||
|
|
@ -144,7 +174,34 @@ def metaDataInstance(showAccounts: bool,
|
|||
'title': instanceTitle,
|
||||
'uri': domainFull,
|
||||
'urls': {},
|
||||
'version': version
|
||||
'version': version,
|
||||
'rules': rulesList,
|
||||
'configuration': {
|
||||
'statuses': {
|
||||
'max_media_attachments': 1
|
||||
},
|
||||
'media_attachments': {
|
||||
'supported_mime_types': [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
'image/avif',
|
||||
'image/svg+xml',
|
||||
'video/webm',
|
||||
'video/mp4',
|
||||
'video/ogv',
|
||||
'audio/ogg',
|
||||
'audio/flac',
|
||||
'audio/mpeg'
|
||||
],
|
||||
'image_size_limit': 10485760,
|
||||
'image_matrix_limit': 16777216,
|
||||
'video_size_limit': 41943040,
|
||||
'video_frame_rate_limit': 60,
|
||||
'video_matrix_limit': 2304000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instance
|
||||
|
|
|
|||
20
migrate.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
|
||||
|
|
@ -23,7 +23,8 @@ from person import getActorJson
|
|||
def _moveFollowingHandlesForAccount(baseDir: str, nickname: str, domain: str,
|
||||
session,
|
||||
httpPrefix: str, cachedWebfingers: {},
|
||||
debug: bool) -> int:
|
||||
debug: bool,
|
||||
signingPrivateKeyPem: str) -> int:
|
||||
"""Goes through all follows for an account and updates any that have moved
|
||||
"""
|
||||
ctr = 0
|
||||
|
|
@ -38,14 +39,14 @@ def _moveFollowingHandlesForAccount(baseDir: str, nickname: str, domain: str,
|
|||
_updateMovedHandle(baseDir, nickname, domain,
|
||||
followHandle, session,
|
||||
httpPrefix, cachedWebfingers,
|
||||
debug)
|
||||
debug, signingPrivateKeyPem)
|
||||
return ctr
|
||||
|
||||
|
||||
def _updateMovedHandle(baseDir: str, nickname: str, domain: str,
|
||||
handle: str, session,
|
||||
httpPrefix: str, cachedWebfingers: {},
|
||||
debug: bool) -> int:
|
||||
debug: bool, signingPrivateKeyPem: str) -> int:
|
||||
"""Check if an account has moved, and if so then alter following.txt
|
||||
for each account.
|
||||
Returns 1 if moved, 0 otherwise
|
||||
|
|
@ -59,7 +60,8 @@ def _updateMovedHandle(baseDir: str, nickname: str, domain: str,
|
|||
handle = handle[1:]
|
||||
wfRequest = webfingerHandle(session, handle,
|
||||
httpPrefix, cachedWebfingers,
|
||||
None, __version__, debug, False)
|
||||
domain, __version__, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
print('updateMovedHandle unable to webfinger ' + handle)
|
||||
return ctr
|
||||
|
|
@ -83,7 +85,8 @@ def _updateMovedHandle(baseDir: str, nickname: str, domain: str,
|
|||
if httpPrefix == 'gnunet':
|
||||
gnunet = True
|
||||
personJson = \
|
||||
getActorJson(domain, personUrl, httpPrefix, gnunet, debug)
|
||||
getActorJson(domain, personUrl, httpPrefix, gnunet, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not personJson:
|
||||
return ctr
|
||||
if not personJson.get('movedTo'):
|
||||
|
|
@ -172,7 +175,7 @@ def _updateMovedHandle(baseDir: str, nickname: str, domain: str,
|
|||
|
||||
def migrateAccounts(baseDir: str, session,
|
||||
httpPrefix: str, cachedWebfingers: {},
|
||||
debug: bool) -> int:
|
||||
debug: bool, signingPrivateKeyPem: str) -> int:
|
||||
"""If followed accounts change then this modifies the
|
||||
following lists for each account accordingly.
|
||||
Returns the number of accounts migrated
|
||||
|
|
@ -188,6 +191,7 @@ def migrateAccounts(baseDir: str, session,
|
|||
ctr += \
|
||||
_moveFollowingHandlesForAccount(baseDir, nickname, domain,
|
||||
session, httpPrefix,
|
||||
cachedWebfingers, debug)
|
||||
cachedWebfingers, debug,
|
||||
signingPrivateKeyPem)
|
||||
break
|
||||
return ctr
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Web Interface Columns"
|
||||
|
||||
|
|
@ -526,6 +526,7 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
"""Converts rss items in a newswire into posts
|
||||
"""
|
||||
if not newswire:
|
||||
print('No newswire to convert')
|
||||
return
|
||||
|
||||
basePath = baseDir + '/accounts/news@' + domain + '/outbox'
|
||||
|
|
@ -542,9 +543,18 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
dateStr = dateStr.replace(' ', 'T')
|
||||
dateStr = dateStr.replace('+00:00', 'Z')
|
||||
else:
|
||||
dateStrWithOffset = \
|
||||
datetime.datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
|
||||
dateStr = dateStrWithOffset.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
try:
|
||||
dateStrWithOffset = \
|
||||
datetime.datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S%z")
|
||||
except BaseException:
|
||||
print('Newswire strptime failed ' + str(dateStr))
|
||||
continue
|
||||
try:
|
||||
dateStr = dateStrWithOffset.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
except BaseException:
|
||||
print('Newswire dateStrWithOffset failed ' +
|
||||
str(dateStrWithOffset))
|
||||
continue
|
||||
|
||||
statusNumber, published = getStatusNumber(dateStr)
|
||||
newPostId = \
|
||||
|
|
@ -702,7 +712,10 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
blog['object']['arrived'])
|
||||
else:
|
||||
if os.path.isfile(filename + '.arrived'):
|
||||
os.remove(filename + '.arrived')
|
||||
try:
|
||||
os.remove(filename + '.arrived')
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
# setting the url here links to the activitypub object
|
||||
# stored locally
|
||||
|
|
@ -750,6 +763,7 @@ def runNewswireDaemon(baseDir: str, httpd,
|
|||
print('Newswire daemon session established')
|
||||
|
||||
# try to update the feeds
|
||||
print('Updating newswire feeds')
|
||||
newNewswire = \
|
||||
getDictFromNewswire(httpd.session, baseDir, domain,
|
||||
httpd.maxNewswirePostsPerSource,
|
||||
|
|
@ -761,16 +775,22 @@ def runNewswireDaemon(baseDir: str, httpd,
|
|||
httpd.systemLanguage)
|
||||
|
||||
if not httpd.newswire:
|
||||
print('Newswire feeds not updated')
|
||||
if os.path.isfile(newswireStateFilename):
|
||||
print('Loading newswire from file')
|
||||
httpd.newswire = loadJson(newswireStateFilename)
|
||||
|
||||
print('Merging with previous newswire')
|
||||
_mergeWithPreviousNewswire(httpd.newswire, newNewswire)
|
||||
|
||||
httpd.newswire = newNewswire
|
||||
if newNewswire:
|
||||
saveJson(httpd.newswire, newswireStateFilename)
|
||||
print('Newswire updated')
|
||||
else:
|
||||
print('No new newswire')
|
||||
|
||||
print('Converting newswire to activitypub format')
|
||||
_convertRSStoActivityPub(baseDir,
|
||||
httpPrefix, domain, port,
|
||||
newNewswire, translate,
|
||||
|
|
@ -792,6 +812,7 @@ def runNewswireDaemon(baseDir: str, httpd,
|
|||
archiveDir = baseDir + '/archive'
|
||||
archiveSubdir = \
|
||||
archiveDir + '/accounts/news@' + domain + '/outbox'
|
||||
print('Archiving news posts')
|
||||
archivePostsForPerson(httpPrefix, 'news',
|
||||
domain, baseDir, 'outbox',
|
||||
archiveSubdir,
|
||||
|
|
|
|||
15
newswire.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Web Interface Columns"
|
||||
|
||||
|
|
@ -192,9 +192,9 @@ def parseFeedDate(pubDate: str) -> str:
|
|||
formats = ("%a, %d %b %Y %H:%M:%S %z",
|
||||
"%a, %d %b %Y %H:%M:%S EST",
|
||||
"%a, %d %b %Y %H:%M:%S UT",
|
||||
"%a, %d %b %Y %H:%M:%S GMT",
|
||||
"%Y-%m-%dT%H:%M:%SZ",
|
||||
"%Y-%m-%dT%H:%M:%S%z")
|
||||
|
||||
publishedDate = None
|
||||
for dateFormat in formats:
|
||||
if ',' in pubDate and ',' not in dateFormat:
|
||||
|
|
@ -207,6 +207,8 @@ def parseFeedDate(pubDate: str) -> str:
|
|||
continue
|
||||
if 'EST' not in pubDate and 'EST' in dateFormat:
|
||||
continue
|
||||
if 'GMT' not in pubDate and 'GMT' in dateFormat:
|
||||
continue
|
||||
if 'EST' in pubDate and 'EST' not in dateFormat:
|
||||
continue
|
||||
if 'UT' not in pubDate and 'UT' in dateFormat:
|
||||
|
|
@ -218,8 +220,6 @@ def parseFeedDate(pubDate: str) -> str:
|
|||
publishedDate = \
|
||||
datetime.strptime(pubDate, dateFormat)
|
||||
except BaseException:
|
||||
print('WARN: unrecognized date format: ' +
|
||||
pubDate + ' ' + dateFormat)
|
||||
continue
|
||||
|
||||
if publishedDate:
|
||||
|
|
@ -238,6 +238,8 @@ def parseFeedDate(pubDate: str) -> str:
|
|||
pubDateStr = str(publishedDate)
|
||||
if not pubDateStr.endswith('+00:00'):
|
||||
pubDateStr += '+00:00'
|
||||
else:
|
||||
print('WARN: unrecognized date format: ' + pubDate)
|
||||
|
||||
return pubDateStr
|
||||
|
||||
|
|
@ -1028,7 +1030,10 @@ def _addBlogsToNewswire(baseDir: str, domain: str, newswire: {},
|
|||
else:
|
||||
# remove the file if there is nothing to moderate
|
||||
if os.path.isfile(newswireModerationFilename):
|
||||
os.remove(newswireModerationFilename)
|
||||
try:
|
||||
os.remove(newswireModerationFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
|
||||
def getDictFromNewswire(session, baseDir: str, domain: str,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Calendar"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0"?>
|
||||
<Ontology xmlns="http://www.w3.org/2002/07/owl#"
|
||||
xml:base="http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xml="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
ontologyIRI="http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl">
|
||||
<Prefix name="" IRI="http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#"/>
|
||||
<Prefix name="cc" IRI="http://creativecommons.org/ns#"/>
|
||||
<Prefix name="dc" IRI="http://purl.org/dc/terms/"/>
|
||||
<Prefix name="owl" IRI="http://www.w3.org/2002/07/owl#"/>
|
||||
<Prefix name="rdf" IRI="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
|
||||
<Prefix name="xml" IRI="http://www.w3.org/XML/1998/namespace"/>
|
||||
<Prefix name="xsd" IRI="http://www.w3.org/2001/XMLSchema#"/>
|
||||
<Prefix name="xsp" IRI="http://www.owl-ontologies.com/2005/08/07/xsp.owl#"/>
|
||||
<Prefix name="foaf" IRI="http://xmlns.com/foaf/0.1/"/>
|
||||
<Prefix name="rdfs" IRI="http://www.w3.org/2000/01/rdf-schema#"/>
|
||||
<Prefix name="swrl" IRI="http://www.w3.org/2003/11/swrl#"/>
|
||||
<Prefix name="vann" IRI="http://purl.org/vocab/vann/"/>
|
||||
<Prefix name="swrlb" IRI="http://www.w3.org/2003/11/swrlb#"/>
|
||||
<Prefix name="protege" IRI="http://protege.stanford.edu/plugins/owl/protege#"/>
|
||||
<Import>http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl</Import>
|
||||
<Import>http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl</Import>
|
||||
<Import>http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl</Import>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="cc:license"/>
|
||||
<IRI>https://www.gnu.org/licenses/agpl-3.0.en.html</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:contributor"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#rachelA</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:contributor"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#simonL</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:creator"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#bernardC</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:description"/>
|
||||
<Literal xml:lang="en">A common vocabulary for digital food platforms</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:issued"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#date">2018-05-28</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:modified"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#date">2019-10-21</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:publisher"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:title"/>
|
||||
<Literal xml:lang="en">Data Food Consortium</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="vann:preferredNamespacePrefix"/>
|
||||
<Literal>dfc</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:comment"/>
|
||||
<Literal xml:lang="en">A common vocabulary for digital food platforms</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="owl:versionInfo"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#decimal">4.0</Literal>
|
||||
</Annotation>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Person"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#bernardC"/>
|
||||
</ClassAssertion>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Organization"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium"/>
|
||||
</ClassAssertion>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Person"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#rachelA"/>
|
||||
</ClassAssertion>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Person"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#simonL"/>
|
||||
</ClassAssertion>
|
||||
</Ontology>
|
||||
|
||||
|
||||
|
||||
<!-- Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi -->
|
||||
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0"?>
|
||||
<rdf:RDF xmlns="http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#"
|
||||
xml:base="http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/terms/"
|
||||
xmlns:owl="http://www.w3.org/2002/07/owl#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xml="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
|
||||
xmlns:xsp="http://www.owl-ontologies.com/2005/08/07/xsp.owl#"
|
||||
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
xmlns:swrl="http://www.w3.org/2003/11/swrl#"
|
||||
xmlns:vann="http://purl.org/vocab/vann/"
|
||||
xmlns:swrlb="http://www.w3.org/2003/11/swrlb#"
|
||||
xmlns:protege="http://protege.stanford.edu/plugins/owl/protege#">
|
||||
|
||||
<owl:Ontology rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl">
|
||||
<rdf:type rdf:resource="http://purl.org/vocommons/voaf#Vocabulary"/>
|
||||
<vann:preferredNamespacePrefix>dfc</vann:preferredNamespacePrefix>
|
||||
<vann:preferredNamespaceUri>http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#</vann:preferredNamespaceUri>
|
||||
<owl:imports rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl"/>
|
||||
<owl:imports rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl"/>
|
||||
<owl:imports rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl"/>
|
||||
<cc:license rdf:resource="https://www.gnu.org/licenses/agpl-3.0.en.html"/>
|
||||
<dc:contributor rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#rachelA"/>
|
||||
<dc:contributor rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#simonL"/>
|
||||
<dc:creator rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#bernardC"/>
|
||||
<dc:description xml:lang="en">A common vocabulary for digital food platforms</dc:description>
|
||||
<dc:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-05-28</dc:issued>
|
||||
<dc:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2019-10-21</dc:modified>
|
||||
<dc:publisher rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium"/>
|
||||
<dc:title xml:lang="en">Data Food Consortium</dc:title>
|
||||
<rdfs:comment xml:lang="en">A common vocabulary for digital food platforms</rdfs:comment>
|
||||
<owl:versionInfo rdf:datatype="http://www.w3.org/2001/XMLSchema#decimal">4.0</owl:versionInfo>
|
||||
</owl:Ontology>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Individuals
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#bernardC -->
|
||||
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#bernardC">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
||||
</rdf:Description>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium -->
|
||||
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Organization"/>
|
||||
</rdf:Description>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#rachelA -->
|
||||
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#rachelA">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
||||
</rdf:Description>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#simonL -->
|
||||
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#simonL">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
|
||||
|
||||
|
||||
<!-- Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi -->
|
||||
|
|
@ -0,0 +1,549 @@
|
|||
<?xml version="1.0"?>
|
||||
<Ontology xmlns="http://www.w3.org/2002/07/owl#"
|
||||
xml:base="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xml="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
ontologyIRI="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl">
|
||||
<Prefix name="" IRI="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl"/>
|
||||
<Prefix name="cc" IRI="http://creativecommons.org/ns#"/>
|
||||
<Prefix name="dc" IRI="http://purl.org/dc/terms/"/>
|
||||
<Prefix name="owl" IRI="http://www.w3.org/2002/07/owl#"/>
|
||||
<Prefix name="rdf" IRI="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
|
||||
<Prefix name="xml" IRI="http://www.w3.org/XML/1998/namespace"/>
|
||||
<Prefix name="xsd" IRI="http://www.w3.org/2001/XMLSchema#"/>
|
||||
<Prefix name="xsp" IRI="http://www.owl-ontologies.com/2005/08/07/xsp.owl#"/>
|
||||
<Prefix name="foaf" IRI="http://xmlns.com/foaf/0.1/"/>
|
||||
<Prefix name="rdfs" IRI="http://www.w3.org/2000/01/rdf-schema#"/>
|
||||
<Prefix name="swrl" IRI="http://www.w3.org/2003/11/swrl#"/>
|
||||
<Prefix name="vann" IRI="http://purl.org/vocab/vann/"/>
|
||||
<Prefix name="swrlb" IRI="http://www.w3.org/2003/11/swrlb#"/>
|
||||
<Prefix name="protege" IRI="http://protege.stanford.edu/plugins/owl/protege#"/>
|
||||
<Import>http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl</Import>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="cc:license"/>
|
||||
<IRI>https://www.gnu.org/licenses/agpl-3.0.en.html</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:contributor"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#rachelA</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:contributor"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#simonL</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:creator"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#bernardC</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:description"/>
|
||||
<Literal xml:lang="en">A common vocabulary for digital food platforms (Product Glossary Part)</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:issued"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#date">2018-05-28</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:modified"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#date">2019-10-21</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:publisher"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:title"/>
|
||||
<Literal xml:lang="en">Data Food Consortium Product</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="vann:preferredNamespacePrefix"/>
|
||||
<Literal>dfc-p</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:comment"/>
|
||||
<Literal xml:lang="en">A common vocabulary for digital food platforms (Product Glossary Part)</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="owl:versionInfo"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#decimal">4.0</Literal>
|
||||
</Annotation>
|
||||
<Declaration>
|
||||
<Class IRI="#Certification"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#DFC_ProductGlossary_Measure"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#DFC_ProductGlossary_Type"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#Dimension"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#GlobalGenericOrigin"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#NatureOrigin"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#PartOrigin"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#Process"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#ProductType"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#TerritorialOrigin"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#Unit"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="#DFC_ProductGlossary_ObjectProperty"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="#measuredBy"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="#measures"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#bernardC"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#rachelA"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="dc:contributor"/>
|
||||
</Declaration>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Certification"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Certification"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#Certification"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Certification"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#Certification"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
<Class IRI="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
<ObjectExactCardinality cardinality="1">
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
</ObjectExactCardinality>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Measure"/>
|
||||
<Class IRI="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Type"/>
|
||||
<Class IRI="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Type"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Type"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Type"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Type"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#DFC_ProductGlossary_Type"/>
|
||||
<ObjectExactCardinality cardinality="1">
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
</ObjectExactCardinality>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Dimension"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Measure"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Dimension"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#measuredBy"/>
|
||||
<Class IRI="#Unit"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#GlobalGenericOrigin"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#GlobalGenericOrigin"/>
|
||||
<ObjectSomeValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#GlobalGenericOrigin"/>
|
||||
</ObjectSomeValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#GlobalGenericOrigin"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#GlobalGenericOrigin"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#NatureOrigin"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#NatureOrigin"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#NatureOrigin"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#NatureOrigin"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#NatureOrigin"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#PartOrigin"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#PartOrigin"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#PartOrigin"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#PartOrigin"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#PartOrigin"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Process"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Process"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#Process"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Process"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#Process"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#ProductType"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Type"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#ProductType"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#ProductType"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#ProductType"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#ProductType"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#TerritorialOrigin"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#TerritorialOrigin"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#TerritorialOrigin"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#TerritorialOrigin"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#TerritorialOrigin"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Unit"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Measure"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Unit"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#measures"/>
|
||||
<Class IRI="#Dimension"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Unit"/>
|
||||
<ObjectExactCardinality cardinality="1">
|
||||
<ObjectProperty IRI="#measures"/>
|
||||
</ObjectExactCardinality>
|
||||
</SubClassOf>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Person"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#bernardC"/>
|
||||
</ClassAssertion>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Organization"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium"/>
|
||||
</ClassAssertion>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Person"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#rachelA"/>
|
||||
</ClassAssertion>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Person"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#simonL"/>
|
||||
</ClassAssertion>
|
||||
<SubObjectPropertyOf>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<ObjectProperty IRI="#DFC_ProductGlossary_ObjectProperty"/>
|
||||
</SubObjectPropertyOf>
|
||||
<SubObjectPropertyOf>
|
||||
<ObjectProperty IRI="#measuredBy"/>
|
||||
<ObjectProperty IRI="#DFC_ProductGlossary_ObjectProperty"/>
|
||||
</SubObjectPropertyOf>
|
||||
<SubObjectPropertyOf>
|
||||
<ObjectProperty IRI="#measures"/>
|
||||
<ObjectProperty IRI="#DFC_ProductGlossary_ObjectProperty"/>
|
||||
</SubObjectPropertyOf>
|
||||
<SubObjectPropertyOf>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<ObjectProperty IRI="#DFC_ProductGlossary_ObjectProperty"/>
|
||||
</SubObjectPropertyOf>
|
||||
<InverseObjectProperties>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
</InverseObjectProperties>
|
||||
<InverseObjectProperties>
|
||||
<ObjectProperty IRI="#measuredBy"/>
|
||||
<ObjectProperty IRI="#measures"/>
|
||||
</InverseObjectProperties>
|
||||
<ObjectPropertyDomain>
|
||||
<ObjectProperty IRI="#generalizes"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</ObjectPropertyDomain>
|
||||
<ObjectPropertyDomain>
|
||||
<ObjectProperty IRI="#measuredBy"/>
|
||||
<Class IRI="#Dimension"/>
|
||||
</ObjectPropertyDomain>
|
||||
<ObjectPropertyDomain>
|
||||
<ObjectProperty IRI="#measures"/>
|
||||
<Class IRI="#Unit"/>
|
||||
</ObjectPropertyDomain>
|
||||
<ObjectPropertyDomain>
|
||||
<ObjectProperty IRI="#specializes"/>
|
||||
<Class IRI="#DFC_ProductGlossary_Facet"/>
|
||||
</ObjectPropertyDomain>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Certification</IRI>
|
||||
<Literal xml:lang="en">certification</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Certification</IRI>
|
||||
<Literal xml:lang="fr">certification</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#DFC_ProductGlossary_Facet</IRI>
|
||||
<Literal xml:lang="en">Subject of the facets thesaurus</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#DFC_ProductGlossary_Facet</IRI>
|
||||
<Literal xml:lang="fr">Sujet du Thésaurus à Facettes</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#DFC_ProductGlossary_Measure</IRI>
|
||||
<Literal xml:lang="fr">thesaurus des unités de mesure</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#DFC_ProductGlossary_Measure</IRI>
|
||||
<Literal xml:lang="en">unit of measures thesaurus</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Dimension</IRI>
|
||||
<Literal xml:lang="en">dimension</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Dimension</IRI>
|
||||
<Literal xml:lang="fr">dimension</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#GlobalGenericOrigin</IRI>
|
||||
<Literal xml:lang="en">Global generic origin</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#GlobalGenericOrigin</IRI>
|
||||
<Literal xml:lang="fr">Origines génériques globales</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#NatureOrigin</IRI>
|
||||
<Literal xml:lang="en">natural "living" origin</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#NatureOrigin</IRI>
|
||||
<Literal xml:lang="fr">source "vivante" d'origine</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#PartOrigin</IRI>
|
||||
<Literal xml:lang="en">part of natural "living" origin concerned</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#PartOrigin</IRI>
|
||||
<Literal xml:lang="fr">partie de la source "vivante" d'origine concernée</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Process</IRI>
|
||||
<Literal xml:lang="en">process applied</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Process</IRI>
|
||||
<Literal xml:lang="fr">procédé appliqué</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#ProductType</IRI>
|
||||
<Literal xml:lang="en">product type (general taxonomy)</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#ProductType</IRI>
|
||||
<Literal xml:lang="fr">type de produit (classification générale)</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#TerritorialOrigin</IRI>
|
||||
<Literal xml:lang="fr">origine territoriale</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#TerritorialOrigin</IRI>
|
||||
<Literal xml:lang="en">territorial origin</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Unit</IRI>
|
||||
<Literal xml:lang="en">unit</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Unit</IRI>
|
||||
<Literal xml:lang="fr">unité</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#generalizes</IRI>
|
||||
<Literal xml:lang="en">generalizes</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#generalizes</IRI>
|
||||
<Literal xml:lang="fr">généralise</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#measuredBy</IRI>
|
||||
<Literal xml:lang="en">measured by</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#measuredBy</IRI>
|
||||
<Literal xml:lang="fr">mesuré en</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#measures</IRI>
|
||||
<Literal xml:lang="en">measures</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#measures</IRI>
|
||||
<Literal xml:lang="fr">mesure</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#specializes</IRI>
|
||||
<Literal xml:lang="en">specializes</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#specializes</IRI>
|
||||
<Literal xml:lang="fr">spécialise</Literal>
|
||||
</AnnotationAssertion>
|
||||
</Ontology>
|
||||
|
||||
|
||||
|
||||
<!-- Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi -->
|
||||
|
||||
|
|
@ -0,0 +1,428 @@
|
|||
<?xml version="1.0"?>
|
||||
<rdf:RDF xmlns="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl"
|
||||
xml:base="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/terms/"
|
||||
xmlns:owl="http://www.w3.org/2002/07/owl#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xml="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
|
||||
xmlns:xsp="http://www.owl-ontologies.com/2005/08/07/xsp.owl#"
|
||||
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
xmlns:swrl="http://www.w3.org/2003/11/swrl#"
|
||||
xmlns:vann="http://purl.org/vocab/vann/"
|
||||
xmlns:swrlb="http://www.w3.org/2003/11/swrlb#"
|
||||
xmlns:protege="http://protege.stanford.edu/plugins/owl/protege#">
|
||||
<owl:Ontology rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl">
|
||||
<rdf:type rdf:resource="http://purl.org/vocommons/voaf#Vocabulary"/>
|
||||
<vann:preferredNamespacePrefix>dfc-p</vann:preferredNamespacePrefix>
|
||||
<vann:preferredNamespaceUri>http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#</vann:preferredNamespaceUri>
|
||||
<owl:imports rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl"/>
|
||||
<cc:license rdf:resource="https://www.gnu.org/licenses/agpl-3.0.en.html"/>
|
||||
<dc:contributor rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#rachelA"/>
|
||||
<dc:contributor rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#simonL"/>
|
||||
<dc:creator rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#bernardC"/>
|
||||
<dc:description xml:lang="en">A common vocabulary for digital food platforms (Product Glossary Part)</dc:description>
|
||||
<dc:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-05-28</dc:issued>
|
||||
<dc:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2019-10-21</dc:modified>
|
||||
<dc:publisher rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium"/>
|
||||
<dc:title xml:lang="en">Data Food Consortium Product</dc:title>
|
||||
<rdfs:comment xml:lang="en">A common vocabulary for digital food platforms (Product Glossary Part)</rdfs:comment>
|
||||
<owl:versionInfo rdf:datatype="http://www.w3.org/2001/XMLSchema#decimal">4.0</owl:versionInfo>
|
||||
</owl:Ontology>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Annotation properties
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://purl.org/dc/terms/contributor -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://purl.org/dc/terms/contributor"/>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Object Properties
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_ObjectProperty -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_ObjectProperty"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes">
|
||||
<rdfs:subPropertyOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_ObjectProperty"/>
|
||||
<owl:inverseOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<rdfs:domain rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
<rdfs:label xml:lang="en">generalizes</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">généralise</rdfs:label>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#measuredBy -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#measuredBy">
|
||||
<rdfs:subPropertyOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_ObjectProperty"/>
|
||||
<owl:inverseOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#measures"/>
|
||||
<rdfs:domain rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Dimension"/>
|
||||
<rdfs:label xml:lang="en">measured by</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">mesuré en</rdfs:label>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#measures -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#measures">
|
||||
<rdfs:subPropertyOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_ObjectProperty"/>
|
||||
<rdfs:domain rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Unit"/>
|
||||
<rdfs:label xml:lang="en">measures</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">mesure</rdfs:label>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes">
|
||||
<rdfs:subPropertyOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_ObjectProperty"/>
|
||||
<rdfs:domain rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
<rdfs:label xml:lang="en">specializes</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">spécialise</rdfs:label>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Classes
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Certification -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Certification">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Certification"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Certification"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">certification</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">certification</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">Subject of the facets thesaurus</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">Sujet du Thésaurus à Facettes</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Measure -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Measure">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
<rdfs:label xml:lang="fr">thesaurus des unités de mesure</rdfs:label>
|
||||
<rdfs:label xml:lang="en">unit of measures thesaurus</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Type -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Type">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Type"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Type"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Dimension -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Dimension">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Measure"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#measuredBy"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Unit"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">dimension</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">dimension</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#GlobalGenericOrigin -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#GlobalGenericOrigin">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:someValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#GlobalGenericOrigin"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#GlobalGenericOrigin"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">Global generic origin</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">Origines génériques globales</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#NatureOrigin -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#NatureOrigin">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#NatureOrigin"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#NatureOrigin"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">natural "living" origin</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">source "vivante" d'origine</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#PartOrigin -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#PartOrigin">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#PartOrigin"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#PartOrigin"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">part of natural "living" origin concerned</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">partie de la source "vivante" d'origine concernée</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Process -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Process">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Process"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Process"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">process applied</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">procédé appliqué</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#ProductType -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#ProductType">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Type"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#ProductType"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#ProductType"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">product type (general taxonomy)</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">type de produit (classification générale)</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#TerritorialOrigin -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#TerritorialOrigin">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Facet"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#generalizes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#TerritorialOrigin"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#specializes"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#TerritorialOrigin"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="fr">origine territoriale</rdfs:label>
|
||||
<rdfs:label xml:lang="en">territorial origin</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Unit -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Unit">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#DFC_ProductGlossary_Measure"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#measures"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#Dimension"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#measures"/>
|
||||
<owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:label xml:lang="en">unit</rdfs:label>
|
||||
<rdfs:label xml:lang="fr">unité</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Individuals
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#bernardC -->
|
||||
|
||||
<owl:NamedIndividual rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#bernardC">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
||||
</owl:NamedIndividual>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium -->
|
||||
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Organization"/>
|
||||
</rdf:Description>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#rachelA -->
|
||||
|
||||
<owl:NamedIndividual rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#rachelA">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
||||
</owl:NamedIndividual>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#simonL -->
|
||||
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#simonL">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
|
||||
|
||||
|
||||
<!-- Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi -->
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
<?xml version="1.0"?>
|
||||
<Ontology xmlns="http://www.w3.org/2002/07/owl#"
|
||||
xml:base="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xml="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
ontologyIRI="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl">
|
||||
<Prefix name="" IRI="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl"/>
|
||||
<Prefix name="cc" IRI="http://creativecommons.org/ns#"/>
|
||||
<Prefix name="dc" IRI="http://purl.org/dc/terms/"/>
|
||||
<Prefix name="owl" IRI="http://www.w3.org/2002/07/owl#"/>
|
||||
<Prefix name="rdf" IRI="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
|
||||
<Prefix name="xml" IRI="http://www.w3.org/XML/1998/namespace"/>
|
||||
<Prefix name="xsd" IRI="http://www.w3.org/2001/XMLSchema#"/>
|
||||
<Prefix name="xsp" IRI="http://www.owl-ontologies.com/2005/08/07/xsp.owl#"/>
|
||||
<Prefix name="foaf" IRI="http://xmlns.com/foaf/0.1/"/>
|
||||
<Prefix name="rdfs" IRI="http://www.w3.org/2000/01/rdf-schema#"/>
|
||||
<Prefix name="swrl" IRI="http://www.w3.org/2003/11/swrl#"/>
|
||||
<Prefix name="vann" IRI="http://purl.org/vocab/vann/"/>
|
||||
<Prefix name="swrlb" IRI="http://www.w3.org/2003/11/swrlb#"/>
|
||||
<Prefix name="protege" IRI="http://protege.stanford.edu/plugins/owl/protege#"/>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="cc:license"/>
|
||||
<IRI>https://www.gnu.org/licenses/agpl-3.0.en.html</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:creator"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#simonL</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:description"/>
|
||||
<Literal xml:lang="en">A common vocabulary for digital food platforms (Technical Part)</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:issued"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#date">2018-05-28</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:modified"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#date">2019-10-21</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:publisher"/>
|
||||
<IRI>http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium</IRI>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="dc:title"/>
|
||||
<Literal xml:lang="en">Data Food Consortium Technical</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="vann:preferredNamespacePrefix"/>
|
||||
<Literal>dfc-t</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:comment"/>
|
||||
<Literal xml:lang="en">A common vocabulary for digital food platforms (Technical Part)</Literal>
|
||||
</Annotation>
|
||||
<Annotation>
|
||||
<AnnotationProperty abbreviatedIRI="owl:versionInfo"/>
|
||||
<Literal datatypeIRI="http://www.w3.org/2001/XMLSchema#decimal">4.0</Literal>
|
||||
</Annotation>
|
||||
<Declaration>
|
||||
<Class IRI="#DFC_DitributedRepresentation"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#Platform"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#RepresentationPivot"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class abbreviatedIRI="foaf:Organization"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<Class abbreviatedIRI="foaf:Person"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#DFC_TechnicalOntology_ObjectProperty"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="#hasPivot"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="#hostedBy"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<ObjectProperty IRI="#represent"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#simonL"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="cc:license"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="dc:creator"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="dc:description"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="dc:issued"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="dc:modified"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="dc:publisher"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="dc:title"/>
|
||||
</Declaration>
|
||||
<Declaration>
|
||||
<AnnotationProperty abbreviatedIRI="vann:preferredNamespacePrefix"/>
|
||||
</Declaration>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Platform"/>
|
||||
<Class IRI="#DFC_DitributedRepresentation"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#Platform"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#hostedBy"/>
|
||||
<Class abbreviatedIRI="owl:Thing"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#RepresentationPivot"/>
|
||||
<Class IRI="#DFC_DitributedRepresentation"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#RepresentationPivot"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#represent"/>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#RepresentationPivot"/>
|
||||
<ObjectMinCardinality cardinality="1">
|
||||
<ObjectProperty IRI="#represent"/>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
</ObjectMinCardinality>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
<Class IRI="#DFC_DitributedRepresentation"/>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#hasPivot"/>
|
||||
<Class IRI="#RepresentationPivot"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
<ObjectAllValuesFrom>
|
||||
<ObjectProperty IRI="#hostedBy"/>
|
||||
<Class IRI="#Platform"/>
|
||||
</ObjectAllValuesFrom>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
<ObjectExactCardinality cardinality="1">
|
||||
<ObjectProperty IRI="#hasPivot"/>
|
||||
<Class IRI="#RepresentationPivot"/>
|
||||
</ObjectExactCardinality>
|
||||
</SubClassOf>
|
||||
<SubClassOf>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
<ObjectExactCardinality cardinality="1">
|
||||
<ObjectProperty IRI="#hostedBy"/>
|
||||
<Class IRI="#Platform"/>
|
||||
</ObjectExactCardinality>
|
||||
</SubClassOf>
|
||||
<DisjointClasses>
|
||||
<Class IRI="#Platform"/>
|
||||
<Class IRI="#RepresentationPivot"/>
|
||||
<Class IRI="#RepresentedThing"/>
|
||||
</DisjointClasses>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Organization"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium"/>
|
||||
</ClassAssertion>
|
||||
<ClassAssertion>
|
||||
<Class abbreviatedIRI="foaf:Person"/>
|
||||
<NamedIndividual IRI="http://static.datafoodconsortium.org/data/publication.rdf#simonL"/>
|
||||
</ClassAssertion>
|
||||
<SubObjectPropertyOf>
|
||||
<ObjectProperty IRI="#hasPivot"/>
|
||||
<ObjectProperty abbreviatedIRI="owl:topObjectProperty"/>
|
||||
</SubObjectPropertyOf>
|
||||
<SubObjectPropertyOf>
|
||||
<ObjectProperty IRI="#hostedBy"/>
|
||||
<ObjectProperty abbreviatedIRI="owl:topObjectProperty"/>
|
||||
</SubObjectPropertyOf>
|
||||
<SubObjectPropertyOf>
|
||||
<ObjectProperty IRI="#represent"/>
|
||||
<ObjectProperty abbreviatedIRI="owl:topObjectProperty"/>
|
||||
</SubObjectPropertyOf>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#DFC_DitributedRepresentation</IRI>
|
||||
<Literal xml:lang="fr">Concepts de réconciliation de représentation distribuée</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#DFC_DitributedRepresentation</IRI>
|
||||
<Literal xml:lang="en">ditributed représentation reconcialition concepts</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:comment"/>
|
||||
<IRI>#Platform</IRI>
|
||||
<Literal xml:lang="fr">Organisation qui heberge la donnée</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#Platform</IRI>
|
||||
<Literal xml:lang="fr">Plateforme</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:comment"/>
|
||||
<IRI>#RepresentationPivot</IRI>
|
||||
<Literal xml:lang="fr">Permet de designer tous les RepresentatedThing qui sont équivalents et d'etre désigné par un RepresentedThing pour connaitre ses équivalence par transitivité</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#RepresentationPivot</IRI>
|
||||
<Literal xml:lang="fr">Pivot de représentation</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:comment"/>
|
||||
<IRI>#RepresentedThing</IRI>
|
||||
<Literal xml:lang="fr">Chose représentée sur une platefome posadant des equivalences sur d'autres plateformes</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#RepresentedThing</IRI>
|
||||
<Literal xml:lang="fr">Chose représentée</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:comment"/>
|
||||
<IRI>#hasPivot</IRI>
|
||||
<Literal xml:lang="fr">possède un point pivot</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#hostedBy</IRI>
|
||||
<Literal xml:lang="fr">hébergé par</Literal>
|
||||
</AnnotationAssertion>
|
||||
<AnnotationAssertion>
|
||||
<AnnotationProperty abbreviatedIRI="rdfs:label"/>
|
||||
<IRI>#represent</IRI>
|
||||
<Literal xml:lang="fr">représente</Literal>
|
||||
</AnnotationAssertion>
|
||||
</Ontology>
|
||||
|
||||
|
||||
|
||||
<!-- Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi -->
|
||||
|
||||
|
|
@ -0,0 +1,291 @@
|
|||
<?xml version="1.0"?>
|
||||
<rdf:RDF xmlns="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl"
|
||||
xml:base="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/terms/"
|
||||
xmlns:owl="http://www.w3.org/2002/07/owl#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:xml="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
|
||||
xmlns:xsp="http://www.owl-ontologies.com/2005/08/07/xsp.owl#"
|
||||
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
xmlns:swrl="http://www.w3.org/2003/11/swrl#"
|
||||
xmlns:vann="http://purl.org/vocab/vann/"
|
||||
xmlns:swrlb="http://www.w3.org/2003/11/swrlb#"
|
||||
xmlns:protege="http://protege.stanford.edu/plugins/owl/protege#">
|
||||
<owl:Ontology rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl">
|
||||
<rdf:type rdf:resource="http://purl.org/vocommons/voaf#Vocabulary"/>
|
||||
<vann:preferredNamespacePrefix>dfc-t</vann:preferredNamespacePrefix>
|
||||
<vann:preferredNamespaceUri>http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#</vann:preferredNamespaceUri>
|
||||
<cc:license rdf:resource="https://www.gnu.org/licenses/agpl-3.0.en.html"/>
|
||||
<dc:creator rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#simonL"/>
|
||||
<dc:description xml:lang="en">A common vocabulary for digital food platforms (Technical Part)</dc:description>
|
||||
<dc:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-05-28</dc:issued>
|
||||
<dc:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2019-10-21</dc:modified>
|
||||
<dc:publisher rdf:resource="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium"/>
|
||||
<dc:title xml:lang="en">Data Food Consortium Technical</dc:title>
|
||||
<rdfs:comment xml:lang="en">A common vocabulary for digital food platforms (Technical Part)</rdfs:comment>
|
||||
<owl:versionInfo rdf:datatype="http://www.w3.org/2001/XMLSchema#decimal">4.0</owl:versionInfo>
|
||||
</owl:Ontology>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Annotation properties
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://creativecommons.org/ns#license -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://creativecommons.org/ns#license"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://purl.org/dc/terms/creator -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://purl.org/dc/terms/creator"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://purl.org/dc/terms/description -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://purl.org/dc/terms/description"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://purl.org/dc/terms/issued -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://purl.org/dc/terms/issued"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://purl.org/dc/terms/modified -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://purl.org/dc/terms/modified"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://purl.org/dc/terms/publisher -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://purl.org/dc/terms/publisher"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://purl.org/dc/terms/title -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://purl.org/dc/terms/title"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://purl.org/vocab/vann/preferredNamespacePrefix -->
|
||||
|
||||
<owl:AnnotationProperty rdf:about="http://purl.org/vocab/vann/preferredNamespacePrefix"/>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Object Properties
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#DFC_TechnicalOntology_ObjectProperty -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#DFC_TechnicalOntology_ObjectProperty"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hasPivot -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hasPivot">
|
||||
<rdfs:subPropertyOf rdf:resource="http://www.w3.org/2002/07/owl#topObjectProperty"/>
|
||||
<rdfs:comment xml:lang="fr">possède un point pivot</rdfs:comment>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hostedBy -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hostedBy">
|
||||
<rdfs:subPropertyOf rdf:resource="http://www.w3.org/2002/07/owl#topObjectProperty"/>
|
||||
<rdfs:label xml:lang="fr">hébergé par</rdfs:label>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#represent -->
|
||||
|
||||
<owl:ObjectProperty rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#represent">
|
||||
<rdfs:subPropertyOf rdf:resource="http://www.w3.org/2002/07/owl#topObjectProperty"/>
|
||||
<rdfs:label xml:lang="fr">représente</rdfs:label>
|
||||
</owl:ObjectProperty>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Classes
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#DFC_DitributedRepresentation -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#DFC_DitributedRepresentation">
|
||||
<rdfs:label xml:lang="fr">Concepts de réconciliation de représentation distribuée</rdfs:label>
|
||||
<rdfs:label xml:lang="en">ditributed représentation reconcialition concepts</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#Platform -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#Platform">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#DFC_DitributedRepresentation"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hostedBy"/>
|
||||
<owl:allValuesFrom rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:comment xml:lang="fr">Organisation qui heberge la donnée</rdfs:comment>
|
||||
<rdfs:label xml:lang="fr">Plateforme</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentationPivot -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentationPivot">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#DFC_DitributedRepresentation"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#represent"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#represent"/>
|
||||
<owl:minQualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minQualifiedCardinality>
|
||||
<owl:onClass rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:comment xml:lang="fr">Permet de designer tous les RepresentatedThing qui sont équivalents et d'etre désigné par un RepresentedThing pour connaitre ses équivalence par transitivité</rdfs:comment>
|
||||
<rdfs:label xml:lang="fr">Pivot de représentation</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing -->
|
||||
|
||||
<owl:Class rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing">
|
||||
<rdfs:subClassOf rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#DFC_DitributedRepresentation"/>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hasPivot"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentationPivot"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hostedBy"/>
|
||||
<owl:allValuesFrom rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#Platform"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hasPivot"/>
|
||||
<owl:qualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:qualifiedCardinality>
|
||||
<owl:onClass rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentationPivot"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:subClassOf>
|
||||
<owl:Restriction>
|
||||
<owl:onProperty rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#hostedBy"/>
|
||||
<owl:qualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:qualifiedCardinality>
|
||||
<owl:onClass rdf:resource="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#Platform"/>
|
||||
</owl:Restriction>
|
||||
</rdfs:subClassOf>
|
||||
<rdfs:comment xml:lang="fr">Chose représentée sur une platefome posadant des equivalences sur d'autres plateformes</rdfs:comment>
|
||||
<rdfs:label xml:lang="fr">Chose représentée</rdfs:label>
|
||||
</owl:Class>
|
||||
|
||||
|
||||
|
||||
<!-- http://xmlns.com/foaf/0.1/Organization -->
|
||||
|
||||
<owl:Class rdf:about="http://xmlns.com/foaf/0.1/Organization"/>
|
||||
|
||||
|
||||
|
||||
<!-- http://xmlns.com/foaf/0.1/Person -->
|
||||
|
||||
<owl:Class rdf:about="http://xmlns.com/foaf/0.1/Person"/>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Individuals
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium -->
|
||||
|
||||
<owl:NamedIndividual rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Organization"/>
|
||||
</owl:NamedIndividual>
|
||||
|
||||
|
||||
|
||||
<!-- http://static.datafoodconsortium.org/data/publication.rdf#simonL -->
|
||||
|
||||
<owl:NamedIndividual rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#simonL">
|
||||
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
||||
</owl:NamedIndividual>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// General axioms
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
-->
|
||||
|
||||
<rdf:Description>
|
||||
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDisjointClasses"/>
|
||||
<owl:members rdf:parseType="Collection">
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#Platform"/>
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentationPivot"/>
|
||||
<rdf:Description rdf:about="http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#RepresentedThing"/>
|
||||
</owl:members>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
|
||||
|
||||
|
||||
<!-- Generated by the OWL API (version 4.5.9.2019-02-01T07:24:44Z) https://github.com/owlcs/owlapi -->
|
||||
|
|
@ -0,0 +1,989 @@
|
|||
{
|
||||
"@context": {
|
||||
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
|
||||
"dfc-b": "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#",
|
||||
"dfc-p": "http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#",
|
||||
"dfc-t": "http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#",
|
||||
"dfc-u": "http://static.datafoodconsortium.org/data/units.json#",
|
||||
"dfc-p:specialize": {
|
||||
"@type": "@id"
|
||||
}
|
||||
},
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#EntirePlace",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Posto intero",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Gesamter Ort",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Lugar completo",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Все место",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "集合場所",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Áit Eintire",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "जगह",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "入口",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Entire Place",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#EntirePlace",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#PrivateRoom",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Private Room",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "الغرفة الخاصة",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Private Room",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Habitación privada",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Stanza privata",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Privatzimmer",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Private Room",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Quarto privado",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Private Room",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Частная комната",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Private Room",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "プライベートルーム",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Seomra na nDaoine",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "निजी कक्ष",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "私人会议室",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Salle privée",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Private Room",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#PrivateRoom",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#HotelRoom",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Hotel Room",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "فندق",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Hotel Room",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Hotel Room",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Camera dell'hotel",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Hotelzimmer",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Hotel Room",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Quarto de Hotel",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Hotel Room",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Номер в отеле",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Hotel Room",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "ホテル ルーム",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Seomra Óstán",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "होटल",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "旅馆",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Hotel Room",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Hotel Room",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#HotelRoom",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#SharedRoom",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Shared Room",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "الغرفة المشتركة",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Shared Room",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Habitación compartida",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Camera condivisa",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Zimmer",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Shared Room",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Quarto compartilhado",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Shared Room",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Общая комната",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Shared Room",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "シェアルーム",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Seomra Comhroinnte",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "साझा कक्ष",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "共有会议室",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Salle partagée",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Shared Room",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#SharedRoom",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Sofa",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Divano",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Sofá",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Диван",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "ソファ",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Toir agus Crainn",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "सोफा",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Sofa",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Sofa",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Boat",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Boat",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Boat",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Boat",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "El barco",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Barca",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Boote",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Boat",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Barco",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Boat",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Лодка",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Boat",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "ボート",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "taiseachas aeir: fliuch",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "नाव",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "B. 博塔",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Boat",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Boat",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Boat",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Barge",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Barrel",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Barco",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Барж",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "バージ",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Toir agus Crainn",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "बार्ज",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "律师协会",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Barge",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Boat",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Tent",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "الخيمة",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Tenda",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Zelt",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Tenda",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Тент",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "テント",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "टेंट",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "答辩",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Tent",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Tent",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Caravan",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Wohnwagen",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Caravana",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Караван",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "キャラバン",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Amharc ar gach eolas",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "कारवां",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "车队",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Caravan",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Caravan",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Hostel",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Ostello",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Albergue",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Хостел",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "ホステル",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "brú",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "छात्रावास",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "人质",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Hostel",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Hostel",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Yurt",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "يوت",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Rind",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt.",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Юрт",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "ユルト",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "taiseachas aeir: fliuch",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "युर्ट",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "导 言",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Yurt",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Yurt",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Tipi",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi di",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Sugestões",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Советы",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "ログイン",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "An tSeapáin",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "टीका",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "注",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Tipi",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#Tipi",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/accommodationTypes.json#RV",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "РВ",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "RVの特長",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "आरवी",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "RV",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/accommodationTypes.json#RV",
|
||||
"@type": "dfc-p:ProductType"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -2,16 +2,16 @@
|
|||
"@context": {
|
||||
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
|
||||
"dfc-b": "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#",
|
||||
"dfc-p": "http://static.datafoodconsortium.org/ontologies/DFC_ProductOntology.owl#",
|
||||
"dfc-p": "http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#",
|
||||
"dfc-t": "http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#",
|
||||
"dfc-u": "http://static.datafoodconsortium.org/data/units.rdf#",
|
||||
"dfc-u": "http://static.datafoodconsortium.org/data/units.json#",
|
||||
"dfc-p:specialize": {
|
||||
"@type": "@id"
|
||||
}
|
||||
},
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#shirt",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#shirt",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "La chemise",
|
||||
|
|
@ -82,11 +82,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/clothesTypes.rdf#shirt",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/clothesTypes.json#shirt",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#belt",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#belt",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Ceinture",
|
||||
|
|
@ -157,11 +157,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#belt",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#belt",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#childrens-clothing",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#childrens-clothing",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Vêtements pour enfants",
|
||||
|
|
@ -232,11 +232,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#childrens-clothing",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#childrens-clothing",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#coat",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#coat",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Manteau",
|
||||
|
|
@ -307,11 +307,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#coat",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#coat",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#dress",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#dress",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Robe",
|
||||
|
|
@ -382,11 +382,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#womens",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#womens",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#shoes",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#shoes",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Des chaussures",
|
||||
|
|
@ -457,11 +457,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#footwear",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#footwear",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#boots",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#boots",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Bottes",
|
||||
|
|
@ -532,11 +532,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#footwear",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#footwear",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#gown",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#gown",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Robe",
|
||||
|
|
@ -607,11 +607,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#gown",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#gown",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#hat",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#hat",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Chapeau",
|
||||
|
|
@ -682,11 +682,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#hat",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#hat",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#hosiery",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#hosiery",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Hosiery",
|
||||
|
|
@ -757,11 +757,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#hosiery",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#hosiery",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#jacket",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#jacket",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Veste",
|
||||
|
|
@ -832,11 +832,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#jacket",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#jacket",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#jeans",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#jeans",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Jeans",
|
||||
|
|
@ -907,11 +907,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#jeans",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#jeans",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#mask",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#mask",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Masquer",
|
||||
|
|
@ -982,11 +982,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#mask",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#mask",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#neckwear",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#neckwear",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Vêtements de cou",
|
||||
|
|
@ -1057,11 +1057,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#neckwear",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#neckwear",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#scarf",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#scarf",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Écharpe",
|
||||
|
|
@ -1132,11 +1132,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#neckwear",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#neckwear",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#suit",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#suit",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Costume",
|
||||
|
|
@ -1207,11 +1207,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#suit",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#suit",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#poncho",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#poncho",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Poncho",
|
||||
|
|
@ -1282,11 +1282,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#poncho",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#poncho",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#cloak",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#cloak",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Manteau",
|
||||
|
|
@ -1357,11 +1357,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#cloak",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#cloak",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#sari",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#sari",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Sari",
|
||||
|
|
@ -1432,11 +1432,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#womens",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#womens",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#sash",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#sash",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Ceinture",
|
||||
|
|
@ -1507,11 +1507,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#sash",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#sash",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#shawl",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#shawl",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Châle",
|
||||
|
|
@ -1582,11 +1582,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#womens",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#womens",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#skirt",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#skirt",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Jupe",
|
||||
|
|
@ -1657,11 +1657,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#womens",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#womens",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#trousers",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#trousers",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Pantalon",
|
||||
|
|
@ -1732,11 +1732,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#trousers",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#trousers",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#shorts",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#shorts",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Shorts",
|
||||
|
|
@ -1807,11 +1807,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#shorts",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#shorts",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#underwear",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#underwear",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Sous-vêtement",
|
||||
|
|
@ -1882,11 +1882,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#underwear",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#underwear",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#socks",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#socks",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Des chaussettes",
|
||||
|
|
@ -1957,11 +1957,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#footwear",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#footwear",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#helmet",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#helmet",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Casque",
|
||||
|
|
@ -2032,11 +2032,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#helmet",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#helmet",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#gloves",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#gloves",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Gants",
|
||||
|
|
@ -2107,11 +2107,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#gloves",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#gloves",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#kurta",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#kurta",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Kurta",
|
||||
|
|
@ -2182,11 +2182,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#kurta",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#kurta",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#sherwani",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#sherwani",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Sherwani",
|
||||
|
|
@ -2257,11 +2257,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#mens",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#mens",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#shalwar-kameez",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#shalwar-kameez",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Shalwar Kameez",
|
||||
|
|
@ -2332,11 +2332,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#womens",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#womens",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#cheongsam",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#cheongsam",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Cheongsam",
|
||||
|
|
@ -2407,11 +2407,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#womens",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#womens",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#áo-bà-ba",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#áo-bà-ba",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Áo bà ba",
|
||||
|
|
@ -2482,11 +2482,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#áo-bà-ba",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#áo-bà-ba",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#áo-dài",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#áo-dài",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Áo dài",
|
||||
|
|
@ -2557,11 +2557,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#áo-dài",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#áo-dài",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#halter-top",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#halter-top",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Halter haut",
|
||||
|
|
@ -2632,11 +2632,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#womens",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#womens",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#sandals",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#sandals",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Des sandales",
|
||||
|
|
@ -2707,11 +2707,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#footwear",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#footwear",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#slippers",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#slippers",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Chaussons",
|
||||
|
|
@ -2782,11 +2782,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#footwear",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#footwear",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://clothes/data/clothesTypes.rdf#kilt",
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#kilt",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Kilt",
|
||||
|
|
@ -2857,7 +2857,157 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://clothes/data/toolTypes.rdf#kilt",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#kilt",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#apron",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Grembiule",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Apres",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Avental",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Абон",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "エプロン",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "An tAthrú",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "एप्रन",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "环境",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Apron",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#apron",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "http://static.datafoodconsortium.org/data/clothesTypes.json#corset",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "en"
|
||||
},
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "ar"
|
||||
},
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "ku"
|
||||
},
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "es"
|
||||
},
|
||||
{
|
||||
"@value": "Corse",
|
||||
"@language": "it"
|
||||
},
|
||||
{
|
||||
"@value": "Korsett",
|
||||
"@language": "de"
|
||||
},
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "sw"
|
||||
},
|
||||
{
|
||||
"@value": "Espartilho",
|
||||
"@language": "pt"
|
||||
},
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "oc"
|
||||
},
|
||||
{
|
||||
"@value": "Корсет",
|
||||
"@language": "ru"
|
||||
},
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "cy"
|
||||
},
|
||||
{
|
||||
"@value": "コルセット",
|
||||
"@language": "ja"
|
||||
},
|
||||
{
|
||||
"@value": "Sraith",
|
||||
"@language": "ga"
|
||||
},
|
||||
{
|
||||
"@value": "कोर्सेट",
|
||||
"@language": "hi"
|
||||
},
|
||||
{
|
||||
"@value": "Cset",
|
||||
"@language": "zh"
|
||||
},
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "fr"
|
||||
},
|
||||
{
|
||||
"@value": "Corset",
|
||||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/toolTypes.rdf#corset",
|
||||
"@type": "dfc-p:ProductType"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@
|
|||
"@context": {
|
||||
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
|
||||
"dfc-b": "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#",
|
||||
"dfc-p": "http://static.datafoodconsortium.org/ontologies/DFC_ProductOntology.owl#",
|
||||
"dfc-p": "http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#",
|
||||
"dfc-t": "http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#",
|
||||
"dfc-u": "http://static.datafoodconsortium.org/data/units.rdf#",
|
||||
"dfc-u": "http://static.datafoodconsortium.org/data/units.json#",
|
||||
"dfc-p:specialize": {
|
||||
"@type": "@id"
|
||||
}
|
||||
},
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#gas-mask",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#gas-mask",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Gas Mask",
|
||||
|
|
@ -82,11 +82,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#body-protection",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#body-protection",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#gas-mask-filter",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#gas-mask-filter",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Gas Mask Filter",
|
||||
|
|
@ -157,11 +157,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#body-protection",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#body-protection",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Bandages",
|
||||
|
|
@ -232,11 +232,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#gauze-wrap",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#gauze-wrap",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Gauze Wrap",
|
||||
|
|
@ -307,11 +307,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#gauze-pad",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#gauze-pad",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Gauze Pad",
|
||||
|
|
@ -382,11 +382,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#nonstick-pad",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#nonstick-pad",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Nonstick Pad",
|
||||
|
|
@ -457,11 +457,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#triangle-bandage",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#triangle-bandage",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Triangle Bandage",
|
||||
|
|
@ -532,11 +532,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#wound-closure-strip",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#wound-closure-strip",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Wound Closure Strip",
|
||||
|
|
@ -607,11 +607,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#paper-tape",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#paper-tape",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Paper Tape",
|
||||
|
|
@ -682,11 +682,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#tape",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#tape",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#plastic-tape",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#plastic-tape",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Plastic Tape",
|
||||
|
|
@ -757,11 +757,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#tape",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#tape",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#examination-gloves",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#examination-gloves",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Examination Gloves",
|
||||
|
|
@ -832,11 +832,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#gloves",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#gloves",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#stick-on-bandage",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#stick-on-bandage",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Stick-on Bandage",
|
||||
|
|
@ -907,11 +907,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#saline-solution",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#saline-solution",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Saline Solution",
|
||||
|
|
@ -982,11 +982,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#fluid",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#fluid",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#antibiotic-ointment",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#antibiotic-ointment",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Antibiotic Ointment",
|
||||
|
|
@ -1057,11 +1057,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#medicine",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#medicine",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#anti-hemorrhagic-agent",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#anti-hemorrhagic-agent",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Anti-hemorrhagic Agent",
|
||||
|
|
@ -1132,11 +1132,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#fluids",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#fluids",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#sunblock",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#sunblock",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Sunblock",
|
||||
|
|
@ -1207,11 +1207,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#body-protection",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#body-protection",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#bandage-shears",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandage-shears",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Bandage Shears",
|
||||
|
|
@ -1282,11 +1282,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bandages",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandages",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#tweezers",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#tweezers",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Tweezers",
|
||||
|
|
@ -1357,11 +1357,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#medical-tools",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#medical-tools",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#protein-bar",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#protein-bar",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Protein Bar",
|
||||
|
|
@ -1432,11 +1432,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#energy",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#energy",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#bandanna",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#bandanna",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Bandanna",
|
||||
|
|
@ -1507,11 +1507,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#body-protection",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#body-protection",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#water-bottle",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#water-bottle",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Water Bottle",
|
||||
|
|
@ -1582,11 +1582,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#fluids",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#fluids",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#ice-pack",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#ice-pack",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Ice Pack",
|
||||
|
|
@ -1657,11 +1657,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#anti-inflamatory",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#anti-inflamatory",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#messenger-bag",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#messenger-bag",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Messenger Bag",
|
||||
|
|
@ -1732,11 +1732,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#bag",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#bag",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#glucose-tablets",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#glucose-tablets",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Glucose tablets",
|
||||
|
|
@ -1807,11 +1807,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#energy",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#energy",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#liquid-antacid-water-mixture",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#liquid-antacid-water-mixture",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Liquid Antacid Water Mixture",
|
||||
|
|
@ -1882,11 +1882,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#fluids",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#fluids",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#re-hydration-mixture",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#re-hydration-mixture",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Re-hydration Mixture",
|
||||
|
|
@ -1957,11 +1957,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#fluids",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#fluids",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#ear-plugs",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#ear-plugs",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "Ear Plugs",
|
||||
|
|
@ -2032,11 +2032,11 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#body-protection",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#body-protection",
|
||||
"@type": "dfc-p:ProductType"
|
||||
},
|
||||
{
|
||||
"@id": "https://medical/data/medicalTypes.rdf#cpr-mask",
|
||||
"@id": "http://static.datafoodconsortium.org/data/medicalTypes.json#cpr-mask",
|
||||
"rdfs:label": [
|
||||
{
|
||||
"@value": "CPR Mask",
|
||||
|
|
@ -2107,7 +2107,7 @@
|
|||
"@language": "ca"
|
||||
}
|
||||
],
|
||||
"dfc-p:specialize": "https://medical/data/medicalTypes.rdf#mask",
|
||||
"dfc-p:specialize": "http://static.datafoodconsortium.org/data/medicalTypes.json#mask",
|
||||
"@type": "dfc-p:ProductType"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||
xmlns:owl="http://www.w3.org/2002/07/owl#">
|
||||
|
||||
<foaf:Person rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#simonL">
|
||||
<foaf:familyName xml:lang="fr">Louvet</foaf:familyName>
|
||||
<foaf:firstName xml:lang="fr">Simon</foaf:firstName>
|
||||
<owl:sameAs rdf:resource="https://orcid.org/0000-0002-3528-6577"/>
|
||||
</foaf:Person>
|
||||
<foaf:Person rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#rachelA">
|
||||
<foaf:familyName xml:lang="fr">Arnould</foaf:familyName>
|
||||
<foaf:firstName xml:lang="fr">Rachel</foaf:firstName>
|
||||
</foaf:Person>
|
||||
<foaf:Person rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#bernardC">
|
||||
<foaf:familyName xml:lang="fr">Chabot</foaf:familyName>
|
||||
<foaf:firstName xml:lang="fr">Bernard</foaf:firstName>
|
||||
</foaf:Person>
|
||||
<foaf:Organization rdf:about="http://static.datafoodconsortium.org/data/publication.rdf#dataFoodConsortium">
|
||||
<foaf:homepage rdf:resource="http://static.datafoodconsortium.org/"/>
|
||||
<foaf:name xml:lang="fr">Data Food Consortium</foaf:name>
|
||||
</foaf:Organization>
|
||||
</rdf:RDF>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@context":{
|
||||
"dfc-p": "http://static.datafoodconsortium.org/ontologies/dfc_ProductGlossary.owl#",
|
||||
"dfc-p": "http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#",
|
||||
"dfc-u":"http://static.datafoodconsortium.org/data/units.rdf#"
|
||||
},
|
||||
"@graph":[
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
xmlns:dfc-u="http://static.datafoodconsortium.org/data/units.rdf#"
|
||||
xmlns:dfc-b="http://static.datafoodconsortium.org/ontologies/DFC_ProductGlossary.owl#">
|
||||
|
||||
<dfc-b:Unit rdf:about="http://static.datafoodconsortium.org/data/unit.rdf#kg">
|
||||
<rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">kilogramme</rdfs:label>
|
||||
</dfc-b:Unit>
|
||||
|
||||
<dfc-b:Unit rdf:about="http://static.datafoodconsortium.org/data/unit.rdf#u">
|
||||
<rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">unité</rdfs:label>
|
||||
</dfc-b:Unit>
|
||||
|
||||
<dfc-b:Unit rdf:about="http://static.datafoodconsortium.org/data/unit.rdf#g">
|
||||
<rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">gramme</rdfs:label>
|
||||
</dfc-b:Unit>
|
||||
|
||||
<dfc-b:Unit rdf:about="http://static.datafoodconsortium.org/data/unit.rdf#l">
|
||||
<rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">litre</rdfs:label>
|
||||
</dfc-b:Unit>
|
||||
|
||||
</rdf:RDF>
|
||||
84
outbox.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Timeline"
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ from posts import outboxMessageCreateWrap
|
|||
from posts import savePostToBox
|
||||
from posts import sendToFollowersThread
|
||||
from posts import sendToNamedAddresses
|
||||
from utils import hasObjectStringType
|
||||
from utils import getBaseContentFromPost
|
||||
from utils import hasObjectDict
|
||||
from utils import getLocalNetworkAddresses
|
||||
|
|
@ -28,18 +29,21 @@ from utils import loadJson
|
|||
from utils import saveJson
|
||||
from utils import acctDir
|
||||
from utils import localActorUrl
|
||||
from utils import hasActor
|
||||
from blocking import isBlockedDomain
|
||||
from blocking import outboxBlock
|
||||
from blocking import outboxUndoBlock
|
||||
from blocking import outboxMute
|
||||
from blocking import outboxUndoMute
|
||||
from media import replaceYouTube
|
||||
from media import replaceTwitter
|
||||
from media import getMediaPath
|
||||
from media import createMediaDirs
|
||||
from inbox import inboxUpdateIndex
|
||||
from announce import outboxAnnounce
|
||||
from announce import outboxUndoAnnounce
|
||||
from follow import outboxUndoFollow
|
||||
from follow import followerApprovalActive
|
||||
from skills import outboxSkills
|
||||
from availability import outboxAvailability
|
||||
from like import outboxLike
|
||||
|
|
@ -49,6 +53,7 @@ from bookmarks import outboxUndoBookmark
|
|||
from delete import outboxDelete
|
||||
from shares import outboxShareUpload
|
||||
from shares import outboxUndoShareUpload
|
||||
from webapp_post import individualPostAsHtml
|
||||
|
||||
|
||||
def _outboxPersonReceiveUpdate(recentPostsCache: {},
|
||||
|
|
@ -69,13 +74,7 @@ def _outboxPersonReceiveUpdate(recentPostsCache: {},
|
|||
return
|
||||
if messageJson['type'] != 'Update':
|
||||
return
|
||||
if not hasObjectDict(messageJson):
|
||||
if debug:
|
||||
print('DEBUG: c2s actor update object is not dict')
|
||||
return
|
||||
if not messageJson['object'].get('type'):
|
||||
if debug:
|
||||
print('DEBUG: c2s actor update - no type')
|
||||
if not hasObjectStringType(messageJson, debug):
|
||||
return
|
||||
if not isinstance(messageJson['object']['type'], str):
|
||||
if debug:
|
||||
|
|
@ -89,9 +88,7 @@ def _outboxPersonReceiveUpdate(recentPostsCache: {},
|
|||
if debug:
|
||||
print('DEBUG: c2s actor update has no "to" field')
|
||||
return
|
||||
if not messageJson.get('actor'):
|
||||
if debug:
|
||||
print('DEBUG: c2s actor update has no actor field')
|
||||
if not hasActor(messageJson, debug):
|
||||
return
|
||||
if not messageJson.get('id'):
|
||||
if debug:
|
||||
|
|
@ -189,12 +186,17 @@ def postMessageToOutbox(session, translate: {},
|
|||
personCache: {}, allowDeletion: bool,
|
||||
proxyType: str, version: str, debug: bool,
|
||||
YTReplacementDomain: str,
|
||||
twitterReplacementDomain: str,
|
||||
showPublishedDateOnly: bool,
|
||||
allowLocalNetworkAccess: bool,
|
||||
city: str, systemLanguage: str,
|
||||
sharedItemsFederatedDomains: [],
|
||||
sharedItemFederationTokens: {},
|
||||
lowBandwidth: bool) -> bool:
|
||||
lowBandwidth: bool,
|
||||
signingPrivateKeyPem: str,
|
||||
peertubeInstances: str, theme: str,
|
||||
maxLikeCount: int,
|
||||
maxRecentPosts: int) -> bool:
|
||||
"""post is received by the outbox
|
||||
Client to server message post
|
||||
https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery
|
||||
|
|
@ -281,6 +283,9 @@ def postMessageToOutbox(session, translate: {},
|
|||
return False
|
||||
# replace youtube, so that google gets less tracking data
|
||||
replaceYouTube(messageJson, YTReplacementDomain, systemLanguage)
|
||||
# replace twitter, so that twitter posts can be shown without
|
||||
# having a twitter account
|
||||
replaceTwitter(messageJson, twitterReplacementDomain, systemLanguage)
|
||||
# https://www.w3.org/TR/activitypub/#create-activity-outbox
|
||||
messageJson['object']['attributedTo'] = messageJson['actor']
|
||||
if messageJson['object'].get('attachment'):
|
||||
|
|
@ -318,7 +323,7 @@ def postMessageToOutbox(session, translate: {},
|
|||
# generate a path for the uploaded image
|
||||
mPath = getMediaPath()
|
||||
mediaPath = mPath + '/' + \
|
||||
createPassword(32) + '.' + fileExtension
|
||||
createPassword(16).lower() + '.' + fileExtension
|
||||
createMediaDirs(baseDir, mPath)
|
||||
mediaFilename = baseDir + '/' + mediaPath
|
||||
# move the uploaded image to its new path
|
||||
|
|
@ -326,6 +331,10 @@ def postMessageToOutbox(session, translate: {},
|
|||
# change the url of the attachment
|
||||
attach['url'] = \
|
||||
httpPrefix + '://' + domainFull + '/' + mediaPath
|
||||
attach['url'] = \
|
||||
attach['url'].replace('/media/',
|
||||
'/system/' +
|
||||
'media_attachments/files/')
|
||||
|
||||
permittedOutboxTypes = ('Create', 'Announce', 'Like', 'Follow', 'Undo',
|
||||
'Update', 'Add', 'Remove', 'Block', 'Delete',
|
||||
|
|
@ -384,7 +393,10 @@ def postMessageToOutbox(session, translate: {},
|
|||
baseDir + '/accounts/' + \
|
||||
postToNickname + '@' + domain + '/.citations.txt'
|
||||
if os.path.isfile(citationsFilename):
|
||||
os.remove(citationsFilename)
|
||||
try:
|
||||
os.remove(citationsFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
# The following activity types get added to the index files
|
||||
indexedActivities = (
|
||||
|
|
@ -404,10 +416,13 @@ def postMessageToOutbox(session, translate: {},
|
|||
if isImageMedia(session, baseDir, httpPrefix,
|
||||
postToNickname, domain,
|
||||
messageJson,
|
||||
translate, YTReplacementDomain,
|
||||
translate,
|
||||
YTReplacementDomain,
|
||||
twitterReplacementDomain,
|
||||
allowLocalNetworkAccess,
|
||||
recentPostsCache, debug, systemLanguage,
|
||||
domainFull, personCache):
|
||||
domainFull, personCache,
|
||||
signingPrivateKeyPem):
|
||||
inboxUpdateIndex('tlmedia', baseDir,
|
||||
postToNickname + '@' + domain,
|
||||
savedFilename, debug)
|
||||
|
|
@ -423,6 +438,37 @@ def postMessageToOutbox(session, translate: {},
|
|||
inboxUpdateIndex(boxNameIndex, baseDir,
|
||||
postToNickname + '@' + domain,
|
||||
savedFilename, debug)
|
||||
|
||||
# regenerate the html
|
||||
useCacheOnly = False
|
||||
pageNumber = 1
|
||||
showIndividualPostIcons = True
|
||||
manuallyApproveFollowers = \
|
||||
followerApprovalActive(baseDir, postToNickname, domain)
|
||||
individualPostAsHtml(signingPrivateKeyPem,
|
||||
False, recentPostsCache,
|
||||
maxRecentPosts,
|
||||
translate, pageNumber,
|
||||
baseDir, session,
|
||||
cachedWebfingers,
|
||||
personCache,
|
||||
postToNickname, domain, port,
|
||||
messageJson, None, True,
|
||||
allowDeletion,
|
||||
httpPrefix, __version__,
|
||||
boxNameIndex,
|
||||
YTReplacementDomain,
|
||||
twitterReplacementDomain,
|
||||
showPublishedDateOnly,
|
||||
peertubeInstances,
|
||||
allowLocalNetworkAccess,
|
||||
theme, systemLanguage,
|
||||
maxLikeCount,
|
||||
boxNameIndex != 'dm',
|
||||
showIndividualPostIcons,
|
||||
manuallyApproveFollowers,
|
||||
False, True, useCacheOnly)
|
||||
|
||||
if outboxAnnounce(recentPostsCache,
|
||||
baseDir, messageJson, debug):
|
||||
if debug:
|
||||
|
|
@ -468,7 +514,8 @@ def postMessageToOutbox(session, translate: {},
|
|||
messageJson, debug,
|
||||
version,
|
||||
sharedItemsFederatedDomains,
|
||||
sharedItemFederationTokens)
|
||||
sharedItemFederationTokens,
|
||||
signingPrivateKeyPem)
|
||||
followersThreads.append(followersThread)
|
||||
|
||||
if debug:
|
||||
|
|
@ -592,5 +639,6 @@ def postMessageToOutbox(session, translate: {},
|
|||
messageJson, debug,
|
||||
version,
|
||||
sharedItemsFederatedDomains,
|
||||
sharedItemFederationTokens)
|
||||
sharedItemFederationTokens,
|
||||
signingPrivateKeyPem)
|
||||
return True
|
||||
|
|
|
|||
260
person.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
|
|
@ -37,6 +37,7 @@ from roles import setRole
|
|||
from roles import setRolesFromList
|
||||
from roles import getActorRolesList
|
||||
from media import processMetaData
|
||||
from utils import replaceUsersWithAt
|
||||
from utils import removeLineEndings
|
||||
from utils import removeDomainPort
|
||||
from utils import getStatusNumber
|
||||
|
|
@ -55,6 +56,7 @@ from utils import acctDir
|
|||
from utils import getUserPaths
|
||||
from utils import getGroupPaths
|
||||
from utils import localActorUrl
|
||||
from utils import dangerousSVG
|
||||
from session import createSession
|
||||
from session import getJson
|
||||
from webfinger import webfingerHandle
|
||||
|
|
@ -175,16 +177,127 @@ def randomizeActorImages(personJson: {}) -> None:
|
|||
baseUrl = personId.split('/users/')[0]
|
||||
nickname = personJson['preferredUsername']
|
||||
personJson['icon']['url'] = \
|
||||
baseUrl + '/accounts/avatars/' + nickname + \
|
||||
baseUrl + '/system/accounts/avatars/' + nickname + \
|
||||
'/avatar' + randStr + '.' + existingExtension
|
||||
lastPartOfFilename = personJson['image']['url'].split('/')[-1]
|
||||
existingExtension = lastPartOfFilename.split('.')[1]
|
||||
randStr = str(randint(10000000000000, 99999999999999)) # nosec
|
||||
personJson['image']['url'] = \
|
||||
baseUrl + '/accounts/headers/' + nickname + \
|
||||
baseUrl + '/system/accounts/headers/' + nickname + \
|
||||
'/image' + randStr + '.' + existingExtension
|
||||
|
||||
|
||||
def getActorUpdateJson(actorJson: {}) -> {}:
|
||||
"""Returns the json for an Person Update
|
||||
"""
|
||||
pubNumber, _ = getStatusNumber()
|
||||
manuallyApprovesFollowers = actorJson['manuallyApprovesFollowers']
|
||||
return {
|
||||
'@context': [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"featured":
|
||||
{
|
||||
"@id": "toot:featured",
|
||||
"@type": "@id"
|
||||
},
|
||||
"featuredTags":
|
||||
{
|
||||
"@id": "toot:featuredTags",
|
||||
"@type": "@id"
|
||||
},
|
||||
"alsoKnownAs":
|
||||
{
|
||||
"@id": "as:alsoKnownAs",
|
||||
"@type": "@id"
|
||||
},
|
||||
"movedTo":
|
||||
{
|
||||
"@id": "as:movedTo",
|
||||
"@type": "@id"
|
||||
},
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value",
|
||||
"IdentityProof": "toot:IdentityProof",
|
||||
"discoverable": "toot:discoverable",
|
||||
"Device": "toot:Device",
|
||||
"Ed25519Signature": "toot:Ed25519Signature",
|
||||
"Ed25519Key": "toot:Ed25519Key",
|
||||
"Curve25519Key": "toot:Curve25519Key",
|
||||
"EncryptedMessage": "toot:EncryptedMessage",
|
||||
"publicKeyBase64": "toot:publicKeyBase64",
|
||||
"deviceId": "toot:deviceId",
|
||||
"claim":
|
||||
{
|
||||
"@type": "@id",
|
||||
"@id": "toot:claim"
|
||||
},
|
||||
"fingerprintKey":
|
||||
{
|
||||
"@type": "@id",
|
||||
"@id": "toot:fingerprintKey"
|
||||
},
|
||||
"identityKey":
|
||||
{
|
||||
"@type": "@id",
|
||||
"@id": "toot:identityKey"
|
||||
},
|
||||
"devices":
|
||||
{
|
||||
"@type": "@id",
|
||||
"@id": "toot:devices"
|
||||
},
|
||||
"messageFranking": "toot:messageFranking",
|
||||
"messageType": "toot:messageType",
|
||||
"cipherText": "toot:cipherText",
|
||||
"suspended": "toot:suspended",
|
||||
"focalPoint":
|
||||
{
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
}
|
||||
}
|
||||
],
|
||||
'id': actorJson['id'] + '#updates/' + pubNumber,
|
||||
'type': 'Update',
|
||||
'actor': actorJson['id'],
|
||||
'to': ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
'cc': [actorJson['id'] + '/followers'],
|
||||
'object': {
|
||||
'id': actorJson['id'],
|
||||
'type': actorJson['type'],
|
||||
'icon': {
|
||||
'type': 'Image',
|
||||
'url': actorJson['icon']['url']
|
||||
},
|
||||
'image': {
|
||||
'type': 'Image',
|
||||
'url': actorJson['image']['url']
|
||||
},
|
||||
'attachment': actorJson['attachment'],
|
||||
'following': actorJson['id'] + '/following',
|
||||
'followers': actorJson['id'] + '/followers',
|
||||
'inbox': actorJson['id'] + '/inbox',
|
||||
'outbox': actorJson['id'] + '/outbox',
|
||||
'featured': actorJson['id'] + '/collections/featured',
|
||||
'featuredTags': actorJson['id'] + '/collections/tags',
|
||||
'preferredUsername': actorJson['preferredUsername'],
|
||||
'name': actorJson['name'],
|
||||
'summary': actorJson['summary'],
|
||||
'url': actorJson['url'],
|
||||
'manuallyApprovesFollowers': manuallyApprovesFollowers,
|
||||
'discoverable': actorJson['discoverable'],
|
||||
'published': actorJson['published'],
|
||||
'devices': actorJson['devices'],
|
||||
"publicKey": actorJson['publicKey'],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def getDefaultPersonContext() -> str:
|
||||
"""Gets the default actor context
|
||||
"""
|
||||
|
|
@ -702,7 +815,7 @@ def personUpgradeActor(baseDir: str, personJson: {},
|
|||
# update domain/@nickname in actors cache
|
||||
actorCacheFilename = \
|
||||
baseDir + '/accounts/cache/actors/' + \
|
||||
personJson['id'].replace('/users/', '/@').replace('/', '#') + \
|
||||
replaceUsersWithAt(personJson['id']).replace('/', '#') + \
|
||||
'.json'
|
||||
if os.path.isfile(actorCacheFilename):
|
||||
saveJson(personJson, actorCacheFilename)
|
||||
|
|
@ -717,7 +830,7 @@ def personLookup(domain: str, path: str, baseDir: str) -> {}:
|
|||
isSharedInbox = False
|
||||
if path == '/inbox' or path == '/users/inbox' or path == '/sharedInbox':
|
||||
# shared inbox actor on @domain@domain
|
||||
path = '/users/' + domain
|
||||
path = '/users/inbox'
|
||||
isSharedInbox = True
|
||||
else:
|
||||
notPersonLookup = ('/inbox', '/outbox', '/outboxarchive',
|
||||
|
|
@ -741,7 +854,8 @@ def personLookup(domain: str, path: str, baseDir: str) -> {}:
|
|||
if not os.path.isfile(filename):
|
||||
return None
|
||||
personJson = loadJson(filename)
|
||||
personUpgradeActor(baseDir, personJson, handle, filename)
|
||||
if not isSharedInbox:
|
||||
personUpgradeActor(baseDir, personJson, handle, filename)
|
||||
# if not personJson:
|
||||
# personJson={"user": "unknown"}
|
||||
return personJson
|
||||
|
|
@ -917,10 +1031,16 @@ def suspendAccount(baseDir: str, nickname: str, domain: str) -> None:
|
|||
|
||||
saltFilename = acctDir(baseDir, nickname, domain) + '/.salt'
|
||||
if os.path.isfile(saltFilename):
|
||||
os.remove(saltFilename)
|
||||
try:
|
||||
os.remove(saltFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
tokenFilename = acctDir(baseDir, nickname, domain) + '/.token'
|
||||
if os.path.isfile(tokenFilename):
|
||||
os.remove(tokenFilename)
|
||||
try:
|
||||
os.remove(tokenFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
suspendedFilename = baseDir + '/accounts/suspended.txt'
|
||||
if os.path.isfile(suspendedFilename):
|
||||
|
|
@ -1023,17 +1143,32 @@ def removeAccount(baseDir: str, nickname: str,
|
|||
if os.path.isdir(baseDir + '/accounts/' + handle):
|
||||
shutil.rmtree(baseDir + '/accounts/' + handle)
|
||||
if os.path.isfile(baseDir + '/accounts/' + handle + '.json'):
|
||||
os.remove(baseDir + '/accounts/' + handle + '.json')
|
||||
try:
|
||||
os.remove(baseDir + '/accounts/' + handle + '.json')
|
||||
except BaseException:
|
||||
pass
|
||||
if os.path.isfile(baseDir + '/wfendpoints/' + handle + '.json'):
|
||||
os.remove(baseDir + '/wfendpoints/' + handle + '.json')
|
||||
try:
|
||||
os.remove(baseDir + '/wfendpoints/' + handle + '.json')
|
||||
except BaseException:
|
||||
pass
|
||||
if os.path.isfile(baseDir + '/keys/private/' + handle + '.key'):
|
||||
os.remove(baseDir + '/keys/private/' + handle + '.key')
|
||||
try:
|
||||
os.remove(baseDir + '/keys/private/' + handle + '.key')
|
||||
except BaseException:
|
||||
pass
|
||||
if os.path.isfile(baseDir + '/keys/public/' + handle + '.pem'):
|
||||
os.remove(baseDir + '/keys/public/' + handle + '.pem')
|
||||
try:
|
||||
os.remove(baseDir + '/keys/public/' + handle + '.pem')
|
||||
except BaseException:
|
||||
pass
|
||||
if os.path.isdir(baseDir + '/sharefiles/' + nickname):
|
||||
shutil.rmtree(baseDir + '/sharefiles/' + nickname)
|
||||
if os.path.isfile(baseDir + '/wfdeactivated/' + handle + '.json'):
|
||||
os.remove(baseDir + '/wfdeactivated/' + handle + '.json')
|
||||
try:
|
||||
os.remove(baseDir + '/wfdeactivated/' + handle + '.json')
|
||||
except BaseException:
|
||||
pass
|
||||
if os.path.isdir(baseDir + '/sharefilesdeactivated/' + nickname):
|
||||
shutil.rmtree(baseDir + '/sharefilesdeactivated/' + nickname)
|
||||
|
||||
|
|
@ -1215,7 +1350,8 @@ def _detectUsersPath(url: str) -> str:
|
|||
|
||||
|
||||
def getActorJson(hostDomain: str, handle: str, http: bool, gnunet: bool,
|
||||
debug: bool, quiet: bool = False) -> ({}, {}):
|
||||
debug: bool, quiet: bool,
|
||||
signingPrivateKeyPem: str) -> ({}, {}):
|
||||
"""Returns the actor json
|
||||
"""
|
||||
if debug:
|
||||
|
|
@ -1302,52 +1438,68 @@ def getActorJson(hostDomain: str, handle: str, http: bool, gnunet: bool,
|
|||
if nickname == 'inbox':
|
||||
nickname = domain
|
||||
|
||||
handle = nickname + '@' + domain
|
||||
wfRequest = webfingerHandle(session, handle,
|
||||
httpPrefix, cachedWebfingers,
|
||||
None, __version__, debug,
|
||||
groupAccount)
|
||||
if not wfRequest:
|
||||
if not quiet:
|
||||
print('getActorJson Unable to webfinger ' + handle)
|
||||
return None, None
|
||||
if not isinstance(wfRequest, dict):
|
||||
if not quiet:
|
||||
print('getActorJson Webfinger for ' + handle +
|
||||
' did not return a dict. ' + str(wfRequest))
|
||||
return None, None
|
||||
|
||||
if not quiet:
|
||||
pprint(wfRequest)
|
||||
|
||||
personUrl = None
|
||||
if wfRequest.get('errors'):
|
||||
if not quiet or debug:
|
||||
print('getActorJson wfRequest error: ' +
|
||||
str(wfRequest['errors']))
|
||||
if hasUsersPath(handle):
|
||||
personUrl = originalActor
|
||||
else:
|
||||
if debug:
|
||||
print('No users path in ' + handle)
|
||||
wfRequest = None
|
||||
|
||||
if '://' in originalActor and \
|
||||
originalActor.lower().endswith('/actor'):
|
||||
if debug:
|
||||
print(originalActor + ' is an instance actor')
|
||||
personUrl = originalActor
|
||||
elif '://' in originalActor and groupAccount:
|
||||
if debug:
|
||||
print(originalActor + ' is a group actor')
|
||||
personUrl = originalActor
|
||||
else:
|
||||
handle = nickname + '@' + domain
|
||||
wfRequest = webfingerHandle(session, handle,
|
||||
httpPrefix, cachedWebfingers,
|
||||
hostDomain, __version__, debug,
|
||||
groupAccount, signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if not quiet:
|
||||
print('getActorJson Unable to webfinger ' + handle)
|
||||
return None, None
|
||||
if not isinstance(wfRequest, dict):
|
||||
if not quiet:
|
||||
print('getActorJson Webfinger for ' + handle +
|
||||
' did not return a dict. ' + str(wfRequest))
|
||||
return None, None
|
||||
|
||||
if not quiet:
|
||||
pprint(wfRequest)
|
||||
|
||||
if wfRequest.get('errors'):
|
||||
if not quiet or debug:
|
||||
print('getActorJson wfRequest error: ' +
|
||||
str(wfRequest['errors']))
|
||||
if hasUsersPath(handle):
|
||||
personUrl = originalActor
|
||||
else:
|
||||
if debug:
|
||||
print('No users path in ' + handle)
|
||||
return None, None
|
||||
|
||||
profileStr = 'https://www.w3.org/ns/activitystreams'
|
||||
headersList = (
|
||||
"activity+json", "ld+json", "jrd+json"
|
||||
)
|
||||
if not personUrl:
|
||||
if not personUrl and wfRequest:
|
||||
personUrl = getUserUrl(wfRequest, 0, debug)
|
||||
if nickname == domain:
|
||||
paths = getUserPaths()
|
||||
for userPath in paths:
|
||||
personUrl = personUrl.replace(userPath, '/actor/')
|
||||
if not personUrl and groupAccount:
|
||||
personUrl = httpPrefix + '://' + domain + '/c/' + nickname
|
||||
if not personUrl:
|
||||
# try single user instance
|
||||
personUrl = httpPrefix + '://' + domain + '/' + nickname
|
||||
headersList = (
|
||||
"ld+json", "jrd+json", "activity+json"
|
||||
)
|
||||
if debug:
|
||||
print('Trying single user instance ' + personUrl)
|
||||
if '/channel/' in personUrl or '/accounts/' in personUrl:
|
||||
headersList = (
|
||||
"ld+json", "jrd+json", "activity+json"
|
||||
|
|
@ -1360,7 +1512,7 @@ def getActorJson(hostDomain: str, handle: str, http: bool, gnunet: bool,
|
|||
'Accept': headerMimeType + '; profile="' + profileStr + '"'
|
||||
}
|
||||
personJson = \
|
||||
getJson(session, personUrl, asHeader, None,
|
||||
getJson(signingPrivateKeyPem, session, personUrl, asHeader, None,
|
||||
debug, __version__, httpPrefix, hostDomain, 20, quiet)
|
||||
if personJson:
|
||||
if not quiet:
|
||||
|
|
@ -1386,12 +1538,24 @@ def getPersonAvatarUrl(baseDir: str, personUrl: str, personCache: {},
|
|||
|
||||
imageExtension = getImageExtensions()
|
||||
for ext in imageExtension:
|
||||
if os.path.isfile(avatarImagePath + '.' + ext):
|
||||
return '/avatars/' + actorStr + '.' + ext
|
||||
elif os.path.isfile(avatarImagePath.lower() + '.' + ext):
|
||||
return '/avatars/' + actorStr.lower() + '.' + ext
|
||||
imFilename = avatarImagePath + '.' + ext
|
||||
imPath = '/avatars/' + actorStr + '.' + ext
|
||||
if not os.path.isfile(imFilename):
|
||||
imFilename = avatarImagePath.lower() + '.' + ext
|
||||
imPath = '/avatars/' + actorStr.lower() + '.' + ext
|
||||
if not os.path.isfile(imFilename):
|
||||
continue
|
||||
if ext != 'svg':
|
||||
return imPath
|
||||
else:
|
||||
content = ''
|
||||
with open(imFilename, 'r') as fp:
|
||||
content = fp.read()
|
||||
if not dangerousSVG(content, False):
|
||||
return imPath
|
||||
|
||||
if personJson.get('icon'):
|
||||
if personJson['icon'].get('url'):
|
||||
return personJson['icon']['url']
|
||||
if '.svg' not in personJson['icon']['url'].lower():
|
||||
return personJson['icon']['url']
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
|
||||
|
|
|
|||
44
pgp.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ from utils import isPGPEncrypted
|
|||
from utils import getFullDomain
|
||||
from utils import getStatusNumber
|
||||
from utils import localActorUrl
|
||||
from utils import replaceUsersWithAt
|
||||
from webfinger import webfingerHandle
|
||||
from posts import getPersonBox
|
||||
from auth import createBasicAuthHeader
|
||||
|
|
@ -333,14 +334,16 @@ def _pgpEncrypt(content: str, recipientPubKey: str) -> str:
|
|||
return encryptResult
|
||||
|
||||
|
||||
def _getPGPPublicKeyFromActor(domain: str, handle: str,
|
||||
def _getPGPPublicKeyFromActor(signingPrivateKeyPem: str,
|
||||
domain: str, handle: str,
|
||||
actorJson: {} = None) -> str:
|
||||
"""Searches tags on the actor to see if there is any PGP
|
||||
public key specified
|
||||
"""
|
||||
if not actorJson:
|
||||
actorJson, asHeader = \
|
||||
getActorJson(domain, handle, False, False, False, True)
|
||||
getActorJson(domain, handle, False, False, False, True,
|
||||
signingPrivateKeyPem)
|
||||
if not actorJson:
|
||||
return None
|
||||
if not actorJson.get('attachment'):
|
||||
|
|
@ -372,18 +375,21 @@ def hasLocalPGPkey() -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def pgpEncryptToActor(domain: str, content: str, toHandle: str) -> str:
|
||||
def pgpEncryptToActor(domain: str, content: str, toHandle: str,
|
||||
signingPrivateKeyPem: str) -> str:
|
||||
"""PGP encrypt a message to the given actor or handle
|
||||
"""
|
||||
# get the actor and extract the pgp public key from it
|
||||
recipientPubKey = _getPGPPublicKeyFromActor(domain, toHandle)
|
||||
recipientPubKey = \
|
||||
_getPGPPublicKeyFromActor(signingPrivateKeyPem, domain, toHandle)
|
||||
if not recipientPubKey:
|
||||
return None
|
||||
# encrypt using the recipient public key
|
||||
return _pgpEncrypt(content, recipientPubKey)
|
||||
|
||||
|
||||
def pgpDecrypt(domain: str, content: str, fromHandle: str) -> str:
|
||||
def pgpDecrypt(domain: str, content: str, fromHandle: str,
|
||||
signingPrivateKeyPem: str) -> str:
|
||||
""" Encrypt using your default pgp key to the given recipient
|
||||
fromHandle can be a handle or actor url
|
||||
"""
|
||||
|
|
@ -394,7 +400,9 @@ def pgpDecrypt(domain: str, content: str, fromHandle: str) -> str:
|
|||
if containsPGPPublicKey(content):
|
||||
pubKey = extractPGPPublicKey(content)
|
||||
else:
|
||||
pubKey = _getPGPPublicKeyFromActor(domain, content, fromHandle)
|
||||
pubKey = \
|
||||
_getPGPPublicKeyFromActor(signingPrivateKeyPem,
|
||||
domain, content, fromHandle)
|
||||
if pubKey:
|
||||
_pgpImportPubKey(pubKey)
|
||||
|
||||
|
|
@ -449,7 +457,8 @@ def pgpPublicKeyUpload(baseDir: str, session,
|
|||
domain: str, port: int,
|
||||
httpPrefix: str,
|
||||
cachedWebfingers: {}, personCache: {},
|
||||
debug: bool, test: str) -> {}:
|
||||
debug: bool, test: str,
|
||||
signingPrivateKeyPem: str) -> {}:
|
||||
if debug:
|
||||
print('pgpPublicKeyUpload')
|
||||
|
||||
|
|
@ -481,7 +490,8 @@ def pgpPublicKeyUpload(baseDir: str, session,
|
|||
print('Getting actor for ' + handle)
|
||||
|
||||
actorJson, asHeader = \
|
||||
getActorJson(domain, handle, False, False, debug, True)
|
||||
getActorJson(domainFull, handle, False, False, debug, True,
|
||||
signingPrivateKeyPem)
|
||||
if not actorJson:
|
||||
if debug:
|
||||
print('No actor returned for ' + handle)
|
||||
|
|
@ -491,7 +501,7 @@ def pgpPublicKeyUpload(baseDir: str, session,
|
|||
print('Actor for ' + handle + ' obtained')
|
||||
|
||||
actor = localActorUrl(httpPrefix, nickname, domainFull)
|
||||
handle = actor.replace('/users/', '/@')
|
||||
handle = replaceUsersWithAt(actor)
|
||||
|
||||
# check that this looks like the correct actor
|
||||
if not actorJson.get('id'):
|
||||
|
|
@ -548,7 +558,8 @@ def pgpPublicKeyUpload(baseDir: str, session,
|
|||
# lookup the inbox for the To handle
|
||||
wfRequest = \
|
||||
webfingerHandle(session, handle, httpPrefix, cachedWebfingers,
|
||||
domain, __version__, debug, False)
|
||||
domain, __version__, debug, False,
|
||||
signingPrivateKeyPem)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: pgp actor update webfinger failed for ' +
|
||||
|
|
@ -563,11 +574,12 @@ def pgpPublicKeyUpload(baseDir: str, session,
|
|||
postToBox = 'outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
(inboxUrl, pubKeyId, pubKey,
|
||||
fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName) = getPersonBox(baseDir, session, wfRequest, personCache,
|
||||
__version__, httpPrefix, nickname,
|
||||
domain, postToBox, 52025)
|
||||
originDomain = domain
|
||||
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
|
||||
displayName, _) = getPersonBox(signingPrivateKeyPem, originDomain,
|
||||
baseDir, session, wfRequest, personCache,
|
||||
__version__, httpPrefix, nickname,
|
||||
domain, postToBox, 35725)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
|
|
|
|||
63
pyjsonld.py
|
|
@ -340,16 +340,17 @@ def parse_link_header(header):
|
|||
return rval
|
||||
r_link_header = r'\s*<([^>]*?)>\s*(?:;\s*(.*))?'
|
||||
for entry in entries:
|
||||
match = re.search(r_link_header, entry)
|
||||
if not match:
|
||||
ldmatch = re.search(r_link_header, entry)
|
||||
if not ldmatch:
|
||||
continue
|
||||
match = match.groups()
|
||||
result = {'target': match[0]}
|
||||
params = match[1]
|
||||
ldmatch = ldmatch.groups()
|
||||
result = {'target': ldmatch[0]}
|
||||
params = ldmatch[1]
|
||||
r_params = r'(.*?)=(?:(?:"([^"]*?)")|([^"]*?))\s*(?:(?:;\s*)|$)'
|
||||
matches = re.findall(r_params, params)
|
||||
for match in matches:
|
||||
result[match[0]] = match[2] if match[1] is None else match[1]
|
||||
for ldmatch in matches:
|
||||
result[ldmatch[0]] = \
|
||||
ldmatch[2] if ldmatch[1] is None else ldmatch[1]
|
||||
rel = result.get('rel', '')
|
||||
if isinstance(rval.get(rel), list):
|
||||
rval[rel].append(result)
|
||||
|
|
@ -1474,30 +1475,30 @@ class JsonLdProcessor(object):
|
|||
continue
|
||||
|
||||
# parse quad
|
||||
match = re.search(quad, line)
|
||||
if match is None:
|
||||
ldmatch = re.search(quad, line)
|
||||
if ldmatch is None:
|
||||
raise JsonLdError(
|
||||
'Error while parsing N-Quads invalid quad.',
|
||||
'jsonld.ParseError', {'line': line_number})
|
||||
match = match.groups()
|
||||
ldmatch = ldmatch.groups()
|
||||
|
||||
# create RDF triple
|
||||
triple = {'subject': {}, 'predicate': {}, 'object': {}}
|
||||
|
||||
# get subject
|
||||
if match[0] is not None:
|
||||
triple['subject'] = {'type': 'IRI', 'value': match[0]}
|
||||
if ldmatch[0] is not None:
|
||||
triple['subject'] = {'type': 'IRI', 'value': ldmatch[0]}
|
||||
else:
|
||||
triple['subject'] = {'type': 'blank node', 'value': match[1]}
|
||||
triple['subject'] = {'type': 'blank node', 'value': ldmatch[1]}
|
||||
|
||||
# get predicate
|
||||
triple['predicate'] = {'type': 'IRI', 'value': match[2]}
|
||||
triple['predicate'] = {'type': 'IRI', 'value': ldmatch[2]}
|
||||
|
||||
# get object
|
||||
if match[3] is not None:
|
||||
triple['object'] = {'type': 'IRI', 'value': match[3]}
|
||||
elif match[4] is not None:
|
||||
triple['object'] = {'type': 'blank node', 'value': match[4]}
|
||||
if ldmatch[3] is not None:
|
||||
triple['object'] = {'type': 'IRI', 'value': ldmatch[3]}
|
||||
elif ldmatch[4] is not None:
|
||||
triple['object'] = {'type': 'blank node', 'value': ldmatch[4]}
|
||||
else:
|
||||
triple['object'] = {'type': 'literal'}
|
||||
replacements = {
|
||||
|
|
@ -1507,24 +1508,24 @@ class JsonLdProcessor(object):
|
|||
'\\r': '\r',
|
||||
'\\\\': '\\'
|
||||
}
|
||||
unescaped = match[5]
|
||||
for match, repl in replacements.items():
|
||||
unescaped = unescaped.replace(match, repl)
|
||||
if match[6] is not None:
|
||||
triple['object']['datatype'] = match[6]
|
||||
elif match[7] is not None:
|
||||
unescaped = ldmatch[5]
|
||||
for ldmatch, repl in replacements.items():
|
||||
unescaped = unescaped.replace(ldmatch, repl)
|
||||
if ldmatch[6] is not None:
|
||||
triple['object']['datatype'] = ldmatch[6]
|
||||
elif ldmatch[7] is not None:
|
||||
triple['object']['datatype'] = RDF_LANGSTRING
|
||||
triple['object']['language'] = match[7]
|
||||
triple['object']['language'] = ldmatch[7]
|
||||
else:
|
||||
triple['object']['datatype'] = XSD_STRING
|
||||
triple['object']['value'] = unescaped
|
||||
|
||||
# get graph name ('@default' is used for the default graph)
|
||||
name = '@default'
|
||||
if match[8] is not None:
|
||||
name = match[8]
|
||||
elif match[9] is not None:
|
||||
name = match[9]
|
||||
if ldmatch[8] is not None:
|
||||
name = ldmatch[8]
|
||||
elif ldmatch[9] is not None:
|
||||
name = ldmatch[9]
|
||||
|
||||
# initialize graph in dataset
|
||||
if name not in dataset:
|
||||
|
|
@ -1623,8 +1624,8 @@ class JsonLdProcessor(object):
|
|||
'\"': '\\"'
|
||||
}
|
||||
escaped = o['value']
|
||||
for match, repl in replacements.items():
|
||||
escaped = escaped.replace(match, repl)
|
||||
for ldmatch, repl in replacements.items():
|
||||
escaped = escaped.replace(ldmatch, repl)
|
||||
quad += '"' + escaped + '"'
|
||||
if o['datatype'] == RDF_LANGSTRING:
|
||||
if o['language']:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "ActivityPub"
|
||||
|
||||
|
|
|
|||
2
roles.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Profile Metadata"
|
||||
|
||||
|
|
|
|||
30
schedule.py
|
|
@ -3,7 +3,7 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Calendar"
|
||||
|
||||
|
|
@ -46,7 +46,10 @@ def _updatePostSchedule(baseDir: str, handle: str, httpd,
|
|||
if deleteSchedulePost:
|
||||
# delete extraneous scheduled posts
|
||||
if os.path.isfile(postFilename):
|
||||
os.remove(postFilename)
|
||||
try:
|
||||
os.remove(postFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
continue
|
||||
# create the new index file
|
||||
indexLines.append(line)
|
||||
|
|
@ -110,14 +113,23 @@ def _updatePostSchedule(baseDir: str, handle: str, httpd,
|
|||
httpd.projectVersion,
|
||||
httpd.debug,
|
||||
httpd.YTReplacementDomain,
|
||||
httpd.twitterReplacementDomain,
|
||||
httpd.showPublishedDateOnly,
|
||||
httpd.allowLocalNetworkAccess,
|
||||
httpd.city, httpd.systemLanguage,
|
||||
httpd.sharedItemsFederatedDomains,
|
||||
httpd.sharedItemFederationTokens,
|
||||
httpd.lowBandwidth):
|
||||
httpd.lowBandwidth,
|
||||
httpd.signingPrivateKeyPem,
|
||||
httpd.peertubeInstances,
|
||||
httpd.themeName,
|
||||
httpd.maxLikeCount,
|
||||
httpd.maxRecentPosts):
|
||||
indexLines.remove(line)
|
||||
os.remove(postFilename)
|
||||
try:
|
||||
os.remove(postFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
continue
|
||||
|
||||
# move to the outbox
|
||||
|
|
@ -185,7 +197,10 @@ def removeScheduledPosts(baseDir: str, nickname: str, domain: str) -> None:
|
|||
scheduleIndexFilename = \
|
||||
acctDir(baseDir, nickname, domain) + '/schedule.index'
|
||||
if os.path.isfile(scheduleIndexFilename):
|
||||
os.remove(scheduleIndexFilename)
|
||||
try:
|
||||
os.remove(scheduleIndexFilename)
|
||||
except BaseException:
|
||||
pass
|
||||
# remove the scheduled posts
|
||||
scheduledDir = acctDir(baseDir, nickname, domain) + '/scheduled'
|
||||
if not os.path.isdir(scheduledDir):
|
||||
|
|
@ -194,6 +209,9 @@ def removeScheduledPosts(baseDir: str, nickname: str, domain: str) -> None:
|
|||
filePath = os.path.join(scheduledDir, scheduledPostFilename)
|
||||
try:
|
||||
if os.path.isfile(filePath):
|
||||
os.remove(filePath)
|
||||
try:
|
||||
os.remove(filePath)
|
||||
except BaseException:
|
||||
pass
|
||||
except BaseException:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@ if [ -d /etc/epicyon ]; then
|
|||
else
|
||||
cd /opt/epicyon || exit 0
|
||||
fi
|
||||
git remote set-url origin https://code.freedombone.net/bashrc/epicyon.git
|
||||
git remote set-url origin https://gitlab.com/bashrc2/epicyon.git
|
||||
git checkout main
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
# License
|
||||
# =======
|
||||
#
|
||||
# Copyright (C) 2020-2021 Bob Mottram <bob@freedombone.net>
|
||||
# Copyright (C) 2020-2021 Bob Mottram <bob@libreserver.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
|
|
|
|||
150
session.py
|
|
@ -3,14 +3,15 @@ __author__ = "Bob Mottram"
|
|||
__license__ = "AGPL3+"
|
||||
__version__ = "1.2.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__email__ = "bob@libreserver.org"
|
||||
__status__ = "Production"
|
||||
__module_group__ = "Core"
|
||||
__module_group__ = "Session"
|
||||
|
||||
import os
|
||||
import requests
|
||||
from utils import urlPermitted
|
||||
from utils import isImageFile
|
||||
from httpsig import createSignedHeader
|
||||
import json
|
||||
from socket import error as SocketError
|
||||
import errno
|
||||
|
|
@ -84,43 +85,23 @@ def urlExists(session, url: str, timeoutSec: int = 3,
|
|||
return False
|
||||
|
||||
|
||||
def getJson(session, url: str, headers: {}, params: {}, debug: bool,
|
||||
version: str = '1.2.0', httpPrefix: str = 'https',
|
||||
domain: str = 'testdomain',
|
||||
timeoutSec: int = 20, quiet: bool = False) -> {}:
|
||||
if not isinstance(url, str):
|
||||
if debug and not quiet:
|
||||
print('url: ' + str(url))
|
||||
print('ERROR: getJson failed, url should be a string')
|
||||
return None
|
||||
sessionParams = {}
|
||||
sessionHeaders = {}
|
||||
if headers:
|
||||
sessionHeaders = headers
|
||||
if params:
|
||||
sessionParams = params
|
||||
sessionHeaders['User-Agent'] = 'Epicyon/' + version
|
||||
if domain:
|
||||
sessionHeaders['User-Agent'] += \
|
||||
'; +' + httpPrefix + '://' + domain + '/'
|
||||
if not session:
|
||||
if not quiet:
|
||||
print('WARN: getJson failed, no session specified for getJson')
|
||||
return None
|
||||
|
||||
if debug:
|
||||
HTTPConnection.debuglevel = 1
|
||||
|
||||
def _getJsonRequest(session, url: str, domainFull: str, sessionHeaders: {},
|
||||
sessionParams: {}, timeoutSec: int,
|
||||
signingPrivateKeyPem: str, quiet: bool, debug: bool) -> {}:
|
||||
"""http GET for json
|
||||
"""
|
||||
try:
|
||||
result = session.get(url, headers=sessionHeaders,
|
||||
params=sessionParams, timeout=timeoutSec)
|
||||
if result.status_code != 200:
|
||||
if result.status_code == 401:
|
||||
print('WARN: getJson Unauthorized url: ' + url)
|
||||
print("WARN: getJson " + url + ' rejected by secure mode')
|
||||
elif result.status_code == 403:
|
||||
print('WARN: getJson Forbidden url: ' + url)
|
||||
elif result.status_code == 404:
|
||||
print('WARN: getJson Not Found url: ' + url)
|
||||
elif result.status_code == 410:
|
||||
print('WARN: getJson no longer available url: ' + url)
|
||||
else:
|
||||
print('WARN: getJson url: ' + url +
|
||||
' failed with error code ' +
|
||||
|
|
@ -151,6 +132,115 @@ def getJson(session, url: str, headers: {}, params: {}, debug: bool,
|
|||
return None
|
||||
|
||||
|
||||
def _getJsonSigned(session, url: str, domainFull: str, sessionHeaders: {},
|
||||
sessionParams: {}, timeoutSec: int,
|
||||
signingPrivateKeyPem: str, quiet: bool, debug: bool) -> {}:
|
||||
"""Authorized fetch - a signed version of GET
|
||||
"""
|
||||
if not domainFull:
|
||||
if debug:
|
||||
print('No sending domain for signed GET')
|
||||
return None
|
||||
if '://' not in url:
|
||||
print('Invalid url: ' + url)
|
||||
return None
|
||||
httpPrefix = url.split('://')[0]
|
||||
toDomainFull = url.split('://')[1]
|
||||
if '/' in toDomainFull:
|
||||
toDomainFull = toDomainFull.split('/')[0]
|
||||
|
||||
if ':' in domainFull:
|
||||
domain = domainFull.split(':')[0]
|
||||
port = domainFull.split(':')[1]
|
||||
else:
|
||||
domain = domainFull
|
||||
if httpPrefix == 'https':
|
||||
port = 443
|
||||
else:
|
||||
port = 80
|
||||
|
||||
if ':' in toDomainFull:
|
||||
toDomain = toDomainFull.split(':')[0]
|
||||
toPort = toDomainFull.split(':')[1]
|
||||
else:
|
||||
toDomain = toDomainFull
|
||||
if httpPrefix == 'https':
|
||||
toPort = 443
|
||||
else:
|
||||
toPort = 80
|
||||
|
||||
if debug:
|
||||
print('Signed GET domain: ' + domain + ' ' + str(port))
|
||||
print('Signed GET toDomain: ' + toDomain + ' ' + str(toPort))
|
||||
print('Signed GET url: ' + url)
|
||||
print('Signed GET httpPrefix: ' + httpPrefix)
|
||||
messageStr = ''
|
||||
withDigest = False
|
||||
if toDomainFull + '/' in url:
|
||||
path = '/' + url.split(toDomainFull + '/')[1]
|
||||
else:
|
||||
path = '/actor'
|
||||
contentType = 'application/activity+json'
|
||||
if sessionHeaders.get('Accept'):
|
||||
contentType = sessionHeaders['Accept']
|
||||
signatureHeaderJson = \
|
||||
createSignedHeader(None, signingPrivateKeyPem, 'actor', domain, port,
|
||||
toDomain, toPort, path, httpPrefix, withDigest,
|
||||
messageStr, contentType)
|
||||
if debug:
|
||||
print('Signed GET signatureHeaderJson ' + str(signatureHeaderJson))
|
||||
# update the session headers from the signature headers
|
||||
sessionHeaders['Host'] = signatureHeaderJson['host']
|
||||
sessionHeaders['Date'] = signatureHeaderJson['date']
|
||||
sessionHeaders['Accept'] = signatureHeaderJson['accept']
|
||||
sessionHeaders['Signature'] = signatureHeaderJson['signature']
|
||||
sessionHeaders['Content-Length'] = '0'
|
||||
# if debug:
|
||||
print('Signed GET sessionHeaders ' + str(sessionHeaders))
|
||||
|
||||
return _getJsonRequest(session, url, domainFull, sessionHeaders,
|
||||
sessionParams, timeoutSec, None, quiet, debug)
|
||||
|
||||
|
||||
def getJson(signingPrivateKeyPem: str,
|
||||
session, url: str, headers: {}, params: {}, debug: bool,
|
||||
version: str = '1.2.0', httpPrefix: str = 'https',
|
||||
domain: str = 'testdomain',
|
||||
timeoutSec: int = 20, quiet: bool = False) -> {}:
|
||||
if not isinstance(url, str):
|
||||
if debug and not quiet:
|
||||
print('url: ' + str(url))
|
||||
print('ERROR: getJson failed, url should be a string')
|
||||
return None
|
||||
sessionParams = {}
|
||||
sessionHeaders = {}
|
||||
if headers:
|
||||
sessionHeaders = headers
|
||||
if params:
|
||||
sessionParams = params
|
||||
sessionHeaders['User-Agent'] = 'Epicyon/' + version
|
||||
if domain:
|
||||
sessionHeaders['User-Agent'] += \
|
||||
'; +' + httpPrefix + '://' + domain + '/'
|
||||
if not session:
|
||||
if not quiet:
|
||||
print('WARN: getJson failed, no session specified for getJson')
|
||||
return None
|
||||
|
||||
if debug:
|
||||
HTTPConnection.debuglevel = 1
|
||||
|
||||
if signingPrivateKeyPem:
|
||||
return _getJsonSigned(session, url, domain,
|
||||
sessionHeaders, sessionParams,
|
||||
timeoutSec, signingPrivateKeyPem,
|
||||
quiet, debug)
|
||||
else:
|
||||
return _getJsonRequest(session, url, domain, sessionHeaders,
|
||||
sessionParams, timeoutSec,
|
||||
None, quiet, debug)
|
||||
|
||||
|
||||
def postJson(httpPrefix: str, domainFull: str,
|
||||
session, postJsonObject: {}, federationList: [],
|
||||
inboxUrl: str, headers: {}, timeoutSec: int = 60,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
name = epicyon
|
||||
version = 1.3.0
|
||||
author = Bob Mottram
|
||||
author_email = bob@freedombone.net
|
||||
author_email = bob@libreserver.org
|
||||
maintainer = Bob Mottram
|
||||
maintainer_email = bob@freedombone.net
|
||||
maintainer_email = bob@libreserver.org
|
||||
description = A modern ActivityPub compliant server implementing both S2S and C2S protocols.
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
|
|
|
|||