Merge branch 'master' into feature/noid/announce

pull/374/head
Maxence Lange 2019-02-21 08:38:04 -01:00 zatwierdzone przez GitHub
commit 8a92d5680a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
91 zmienionych plików z 3425 dodań i 5054 usunięć

Wyświetl plik

@ -59,8 +59,8 @@ lint-fix:
# Cleaning
clean:
rm -rf $(build_dir)
rm -f js/social.js
rm -f js/social.js.map
rm -fr js/
mkdir js/
clean-dev:
rm -rf node_modules

Wyświetl plik

@ -54,11 +54,16 @@ return [
['name' => 'ActivityPub#followers', 'url' => '/@{username}/followers', 'verb' => 'GET'],
['name' => 'ActivityPub#following', 'url' => '/@{username}/following', 'verb' => 'GET'],
['name' => 'OStatus#subscribe', 'url' => '/ostatus/follow/{uri}', 'verb' => 'GET'],
['name' => 'OStatus#followRemote', 'url' => '/api/v1/ostatus/followRemote/{local}', 'verb' => 'GET'],
['name' => 'OStatus#getLink', 'url' => '/api/v1/ostatus/link/{local}/{account}', 'verb' => 'GET'],
['name' => 'SocialPub#displayPost', 'url' => '/@{username}/{postId}', 'verb' => 'GET'],
['name' => 'Local#streamHome', 'url' => '/api/v1/stream/home', 'verb' => 'GET'],
['name' => 'Local#streamNotifications', 'url' => '/api/v1/stream/notifications', 'verb' => 'GET'],
['name' => 'Local#streamTimeline', 'url' => '/api/v1/stream/timeline', 'verb' => 'GET'],
['name' => 'Local#streamTag', 'url' => '/api/v1/stream/tag/{hashtag}/', 'verb' => 'GET'],
['name' => 'Local#streamFederated', 'url' => '/api/v1/stream/federated', 'verb' => 'GET'],
['name' => 'Local#streamDirect', 'url' => '/api/v1/stream/direct', 'verb' => 'GET'],
['name' => 'Local#streamAccount', 'url' => '/api/v1/account/{username}/stream', 'verb' => 'GET'],
@ -82,6 +87,8 @@ return [
['name' => 'Local#globalActorAvatar', 'url' => '/api/v1/global/actor/avatar', 'verb' => 'GET'],
['name' => 'Local#globalAccountsSearch', 'url' => '/api/v1/global/accounts/search', 'verb' => 'GET'],
['name' => 'Local#search', 'url' => '/api/v1/search', 'verb' => 'GET'],
['name' => 'Local#documentsCache', 'url' => '/api/v1/documents/cache', 'verb' => 'POST'],
['name' => 'Queue#asyncForRequest', 'url' => CurlService::ASYNC_REQUEST_TOKEN, 'verb' => 'POST'],

Wyświetl plik

@ -29,7 +29,6 @@ OC.L10N.register(
"posts" : "Публикации",
"following" : "последователи",
"followers" : "Последователи",
"No accounts found" : "Не е открит профил",
"Searching for" : "Търсене за",
"No posts found" : "Не са открити публикации",
"Posts from people you follow will show up here" : "Публикации от хора, които следваш ще откриеш тук",

Wyświetl plik

@ -27,7 +27,6 @@
"posts" : "Публикации",
"following" : "последователи",
"followers" : "Последователи",
"No accounts found" : "Не е открит профил",
"Searching for" : "Търсене за",
"No posts found" : "Не са открити публикации",
"Posts from people you follow will show up here" : "Публикации от хора, които следваш ще откриеш тук",

Wyświetl plik

@ -35,7 +35,6 @@ OC.L10N.register(
"posts" : "missatges",
"following" : "seguint",
"followers" : "seguidors",
"No accounts found" : "Cap compte trobat",
"Searching for" : "Buscant",
"No posts found" : "No s'han trobat missatges",
"Posts from people you follow will show up here" : "Missatges de persones que segueix es mostraràn aquí",

Wyświetl plik

@ -33,7 +33,6 @@
"posts" : "missatges",
"following" : "seguint",
"followers" : "seguidors",
"No accounts found" : "Cap compte trobat",
"Searching for" : "Buscant",
"No posts found" : "No s'han trobat missatges",
"Posts from people you follow will show up here" : "Missatges de persones que segueix es mostraràn aquí",

Wyświetl plik

@ -35,7 +35,8 @@ OC.L10N.register(
"posts" : "příspěvky",
"following" : "následují",
"followers" : "odběratelé",
"No accounts found" : "Nenalezeny žádné účty",
"No results found" : "Nenalezeny žádné výsledky",
"There were no results for your search:" : "Pro vaše hledání nejsou žádné výsledky:",
"Searching for" : "Hledání",
"No posts found" : "Nenalezeny žádné příspěvky",
"Posts from people you follow will show up here" : "Příspěvky od lidí, které odebíráte, se zobrazí zde",
@ -45,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Příspěvky od ostatních lidí na této instanci se zobrazí zde",
"No global posts found" : "Nenalezeny žádné globální příspěvky",
"Posts from federated instances will show up here" : "Příspěvky z federovaných instancí se zobrazí zde",
"No posts found for this tag" : "Nenalezeny žádné příspěvky s tímto štítkem",
"User not found" : "Uživatel nenalezen",
"Sorry, we could not find the account of {userId}" : "Je nám líto, nepodařilo se nalézt účet {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud se stává součástí federovaných sociálních sítí!",

Wyświetl plik

@ -33,7 +33,8 @@
"posts" : "příspěvky",
"following" : "následují",
"followers" : "odběratelé",
"No accounts found" : "Nenalezeny žádné účty",
"No results found" : "Nenalezeny žádné výsledky",
"There were no results for your search:" : "Pro vaše hledání nejsou žádné výsledky:",
"Searching for" : "Hledání",
"No posts found" : "Nenalezeny žádné příspěvky",
"Posts from people you follow will show up here" : "Příspěvky od lidí, které odebíráte, se zobrazí zde",
@ -43,6 +44,7 @@
"Posts from other people on this instance will show up here" : "Příspěvky od ostatních lidí na této instanci se zobrazí zde",
"No global posts found" : "Nenalezeny žádné globální příspěvky",
"Posts from federated instances will show up here" : "Příspěvky z federovaných instancí se zobrazí zde",
"No posts found for this tag" : "Nenalezeny žádné příspěvky s tímto štítkem",
"User not found" : "Uživatel nenalezen",
"Sorry, we could not find the account of {userId}" : "Je nám líto, nepodařilo se nalézt účet {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud se stává součástí federovaných sociálních sítí!",

Wyświetl plik

@ -35,7 +35,8 @@ OC.L10N.register(
"posts" : "Posts",
"following" : "Folge ich",
"followers" : "Followers",
"No accounts found" : "Keine Konten gefunden",
"No results found" : "Keine Ergebnisse gefunden",
"There were no results for your search:" : "Es gibt keine Ergebnisse für Deine Suche:",
"Searching for" : "Suchen nach",
"No posts found" : "Keine Posts gefunden",
"Posts from people you follow will show up here" : "Posts von Personen denen Du folgst werden hier angezeigt",
@ -45,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Posts von anderen Personen dieser Instanz werden hier angezeigt",
"No global posts found" : "Keine globalen Posts gefunden",
"Posts from federated instances will show up here" : "Posts von federierten Instanzen werden hier angezeigt",
"No posts found for this tag" : "Keine Posts für dieses Schlagwort gefunden",
"User not found" : "Benutzer nicht gefunden",
"Sorry, we could not find the account of {userId}" : "Das Konto von {userId} konnte nicht gefunden werden",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud wird Teil des Verbundes sozialer Netzwerke!",

Wyświetl plik

@ -33,7 +33,8 @@
"posts" : "Posts",
"following" : "Folge ich",
"followers" : "Followers",
"No accounts found" : "Keine Konten gefunden",
"No results found" : "Keine Ergebnisse gefunden",
"There were no results for your search:" : "Es gibt keine Ergebnisse für Deine Suche:",
"Searching for" : "Suchen nach",
"No posts found" : "Keine Posts gefunden",
"Posts from people you follow will show up here" : "Posts von Personen denen Du folgst werden hier angezeigt",
@ -43,6 +44,7 @@
"Posts from other people on this instance will show up here" : "Posts von anderen Personen dieser Instanz werden hier angezeigt",
"No global posts found" : "Keine globalen Posts gefunden",
"Posts from federated instances will show up here" : "Posts von federierten Instanzen werden hier angezeigt",
"No posts found for this tag" : "Keine Posts für dieses Schlagwort gefunden",
"User not found" : "Benutzer nicht gefunden",
"Sorry, we could not find the account of {userId}" : "Das Konto von {userId} konnte nicht gefunden werden",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud wird Teil des Verbundes sozialer Netzwerke!",

Wyświetl plik

@ -35,7 +35,8 @@ OC.L10N.register(
"posts" : "Posts",
"following" : "Folge ich",
"followers" : "Followers",
"No accounts found" : "Keine Konten gefunden",
"No results found" : "Keine Ergebnisse gefunden",
"There were no results for your search:" : "Es gibt keine Ergebnisse für Ihre Suche:",
"Searching for" : "Suchen nach",
"No posts found" : "Keine Posts gefunden",
"Posts from people you follow will show up here" : "Posts von Personen denen Sie folgen werden hier angezeigt",
@ -45,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Posts von anderen Personen dieser Instanz werden hier angezeigt",
"No global posts found" : "Keine globalen Posts gefunden",
"Posts from federated instances will show up here" : "Posts von federierten Instanzen werden hier angezeigt",
"No posts found for this tag" : "Keine Posts für dieses Schlagwort gefunden",
"User not found" : "Benutzer nicht gefunden",
"Sorry, we could not find the account of {userId}" : "Das Konto von {userId} konnte nicht gefunden werden",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud wird Teil des Verbundes sozialer Netzwerke! ",

Wyświetl plik

@ -33,7 +33,8 @@
"posts" : "Posts",
"following" : "Folge ich",
"followers" : "Followers",
"No accounts found" : "Keine Konten gefunden",
"No results found" : "Keine Ergebnisse gefunden",
"There were no results for your search:" : "Es gibt keine Ergebnisse für Ihre Suche:",
"Searching for" : "Suchen nach",
"No posts found" : "Keine Posts gefunden",
"Posts from people you follow will show up here" : "Posts von Personen denen Sie folgen werden hier angezeigt",
@ -43,6 +44,7 @@
"Posts from other people on this instance will show up here" : "Posts von anderen Personen dieser Instanz werden hier angezeigt",
"No global posts found" : "Keine globalen Posts gefunden",
"Posts from federated instances will show up here" : "Posts von federierten Instanzen werden hier angezeigt",
"No posts found for this tag" : "Keine Posts für dieses Schlagwort gefunden",
"User not found" : "Benutzer nicht gefunden",
"Sorry, we could not find the account of {userId}" : "Das Konto von {userId} konnte nicht gefunden werden",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud wird Teil des Verbundes sozialer Netzwerke! ",

Wyświetl plik

@ -35,7 +35,6 @@ OC.L10N.register(
"posts" : "entradas",
"following" : "siguiendo",
"followers" : "seguidores",
"No accounts found" : "No se sigue a ninguna cuenta",
"Searching for" : "Buscando",
"No posts found" : "No se han encontrado entradas",
"Posts from people you follow will show up here" : "Las entradas de gente a la que sigues aparecerán aquí",

Wyświetl plik

@ -33,7 +33,6 @@
"posts" : "entradas",
"following" : "siguiendo",
"followers" : "seguidores",
"No accounts found" : "No se sigue a ninguna cuenta",
"Searching for" : "Buscando",
"No posts found" : "No se han encontrado entradas",
"Posts from people you follow will show up here" : "Las entradas de gente a la que sigues aparecerán aquí",

30
l10n/es_AR.js 100644
Wyświetl plik

@ -0,0 +1,30 @@
OC.L10N.register(
"social",
{
"Social" : "Social",
"🎉 Nextcloud becomes part of the federated social networks!" : "🎉 ¡Nextcloud se convierte en parte de las redes sociales federadas!",
"Social needs the .well-known automatic discovery to be properly set up. If Nextcloud is not installed in the root of the domain, it is often the case that Nextcloud can't configure this automatically. To use Social, the admin of this Nextcloud instance needs to manually configure the .well-known redirects: " : "Lo social necesita que el descubrimiento automático bien conocido se configure correctamente. Si Nextcloud no está instalado en la raíz del dominio, a menudo es el caso que Nextcloud no puede configurar esto automáticamente. Para usar Social, el administrador de esta instancia de Nextcloud necesita configurar manualmente los redireccionamientos conocidos.",
"Open documentation" : "Abrir documentación",
"Social app setup" : "Configuración de la aplicación social",
"Finish setup" : "Terminar la configuración",
"Home" : "Casa",
"Direct messages" : "Mensajes directos",
"Profile" : "Perfil",
"Local timeline" : "Línea de tiempo local",
"Global timeline" : "Línea de tiempo global",
"Post publicly" : "Publicar públicamente",
"Post to followers" : "Publicar en seguidores",
"Post to recipients" : "Publicar en destinatarios",
"Post unlisted" : "Publicar sin listar",
"Direct" : "Directo",
"Post to mentioned users only" : "Publicar solo para los usuarios mencionados",
"Unlisted" : "No listado",
"Do not post to public timelines" : "No publicar en líneas de tiempo públicas",
"Followers" : "Seguidores",
"Post to followers only" : "Publicar solo en seguidores",
"Public" : "Público",
"Follow" : "Seguir",
"posts" : "publicaciones",
"following" : "siguiendo"
},
"nplurals=2; plural=(n != 1);");

28
l10n/es_AR.json 100644
Wyświetl plik

@ -0,0 +1,28 @@
{ "translations": {
"Social" : "Social",
"🎉 Nextcloud becomes part of the federated social networks!" : "🎉 ¡Nextcloud se convierte en parte de las redes sociales federadas!",
"Social needs the .well-known automatic discovery to be properly set up. If Nextcloud is not installed in the root of the domain, it is often the case that Nextcloud can't configure this automatically. To use Social, the admin of this Nextcloud instance needs to manually configure the .well-known redirects: " : "Lo social necesita que el descubrimiento automático bien conocido se configure correctamente. Si Nextcloud no está instalado en la raíz del dominio, a menudo es el caso que Nextcloud no puede configurar esto automáticamente. Para usar Social, el administrador de esta instancia de Nextcloud necesita configurar manualmente los redireccionamientos conocidos.",
"Open documentation" : "Abrir documentación",
"Social app setup" : "Configuración de la aplicación social",
"Finish setup" : "Terminar la configuración",
"Home" : "Casa",
"Direct messages" : "Mensajes directos",
"Profile" : "Perfil",
"Local timeline" : "Línea de tiempo local",
"Global timeline" : "Línea de tiempo global",
"Post publicly" : "Publicar públicamente",
"Post to followers" : "Publicar en seguidores",
"Post to recipients" : "Publicar en destinatarios",
"Post unlisted" : "Publicar sin listar",
"Direct" : "Directo",
"Post to mentioned users only" : "Publicar solo para los usuarios mencionados",
"Unlisted" : "No listado",
"Do not post to public timelines" : "No publicar en líneas de tiempo públicas",
"Followers" : "Seguidores",
"Post to followers only" : "Publicar solo en seguidores",
"Public" : "Público",
"Follow" : "Seguir",
"posts" : "publicaciones",
"following" : "siguiendo"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

Wyświetl plik

@ -1,18 +0,0 @@
OC.L10N.register(
"social",
{
"Open documentation" : "Ireki dokumentazioa",
"Finish setup" : "Amaitu ezarpena",
"Home" : "Hasiera",
"Direct messages" : "Mezu zuzenak",
"Profile" : "Profila",
"Direct" : "Zuzena",
"Followers" : "Jarraitzaileak",
"Public" : "Publikoa",
"Following" : "Jarraitzen",
"Follow" : "Jarraitu",
"following" : "jarraitzen",
"No accounts found" : "Ez da konturik aurkitu",
"User not found" : "Ez da erabiltzailea aurkitu"
},
"nplurals=2; plural=(n != 1);");

Wyświetl plik

@ -1,16 +0,0 @@
{ "translations": {
"Open documentation" : "Ireki dokumentazioa",
"Finish setup" : "Amaitu ezarpena",
"Home" : "Hasiera",
"Direct messages" : "Mezu zuzenak",
"Profile" : "Profila",
"Direct" : "Zuzena",
"Followers" : "Jarraitzaileak",
"Public" : "Publikoa",
"Following" : "Jarraitzen",
"Follow" : "Jarraitu",
"following" : "jarraitzen",
"No accounts found" : "Ez da konturik aurkitu",
"User not found" : "Ez da erabiltzailea aurkitu"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

Wyświetl plik

@ -31,7 +31,8 @@ OC.L10N.register(
"posts" : "julkaisut",
"following" : "seuraa",
"followers" : "seuraajat",
"No accounts found" : "Tilejä ei löytynyt",
"No results found" : "Tuloksia ei löytynyt",
"There were no results for your search:" : "Hakusi ei palauttanut tuloksia:",
"Searching for" : "Haetaan",
"No posts found" : "Julkaisuja ei löytynyt",
"Posts from people you follow will show up here" : "Seuraamiesi käyttäjien julkaisut näkyvät täällä",
@ -41,6 +42,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Muiden ihmisten tähän asennukseen kirjoitetut julkaisut näkyvät täällä",
"No global posts found" : "Yleisiä julkaisuja ei löytynyt",
"Posts from federated instances will show up here" : "Yhteenliitettyihin asennuksiin tehdyt julkaisut näkyvät täällä",
"User not found" : "Käyttäjää ei löytynyt",
"Nextcloud becomes part of the federated social networks!" : "Nextcloudista tulee yhteenliitettyjen sosiaalisten verkostojen osa!",
"We automatically created a Social account for you. Your Social ID is the same as your federated cloud ID:" : "Loimme sinulle automaattisesti Sosiaalinen -tilin. Sinun Social ID on sama kun yhteenliitettyjen pilvien ID:",
"Since you are new to Social, start by following the official Nextcloud account so you don't miss any news" : "Koska olet uusi käyttäjä, aloita seuraamalla virallista Nextcloud -tiliä ettet jää uutisista paitsi",

Wyświetl plik

@ -29,7 +29,8 @@
"posts" : "julkaisut",
"following" : "seuraa",
"followers" : "seuraajat",
"No accounts found" : "Tilejä ei löytynyt",
"No results found" : "Tuloksia ei löytynyt",
"There were no results for your search:" : "Hakusi ei palauttanut tuloksia:",
"Searching for" : "Haetaan",
"No posts found" : "Julkaisuja ei löytynyt",
"Posts from people you follow will show up here" : "Seuraamiesi käyttäjien julkaisut näkyvät täällä",
@ -39,6 +40,7 @@
"Posts from other people on this instance will show up here" : "Muiden ihmisten tähän asennukseen kirjoitetut julkaisut näkyvät täällä",
"No global posts found" : "Yleisiä julkaisuja ei löytynyt",
"Posts from federated instances will show up here" : "Yhteenliitettyihin asennuksiin tehdyt julkaisut näkyvät täällä",
"User not found" : "Käyttäjää ei löytynyt",
"Nextcloud becomes part of the federated social networks!" : "Nextcloudista tulee yhteenliitettyjen sosiaalisten verkostojen osa!",
"We automatically created a Social account for you. Your Social ID is the same as your federated cloud ID:" : "Loimme sinulle automaattisesti Sosiaalinen -tilin. Sinun Social ID on sama kun yhteenliitettyjen pilvien ID:",
"Since you are new to Social, start by following the official Nextcloud account so you don't miss any news" : "Koska olet uusi käyttäjä, aloita seuraamalla virallista Nextcloud -tiliä ettet jää uutisista paitsi",

Wyświetl plik

@ -20,6 +20,7 @@ OC.L10N.register(
"Post publicly" : "Poster publiquement",
"Post to followers" : "Poster aux abonnés",
"Post to recipients" : "Poster aux destinataires",
"Post unlisted" : "Post non répertorié",
"Direct" : "Direct",
"Post to mentioned users only" : "Poster aux utilisateurs mentionnées seulement",
"Unlisted" : "Non listé",
@ -34,7 +35,8 @@ OC.L10N.register(
"posts" : "messages",
"following" : "suivi",
"followers" : "abonnés",
"No accounts found" : "Aucun compte trouvé",
"No results found" : "Aucun résultat trouvé",
"There were no results for your search:" : "Aucun résultat n'a été trouvé pour votre recherche :",
"Searching for" : "Recherche de",
"No posts found" : "Aucun message trouvé",
"Posts from people you follow will show up here" : "Les messages des personnes que vous suivez apparaitront ici",
@ -44,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Les messages d'autres personnes de cette instance apparaitront ici",
"No global posts found" : "Aucun message global trouvé",
"Posts from federated instances will show up here" : "Les messages des instances fédérées apparaitront ici",
"No posts found for this tag" : "Aucun post trouvé avec cette étiquette",
"User not found" : "Utilisateur non trouvé",
"Sorry, we could not find the account of {userId}" : "Désolé, impossible de trouver le compte {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud fait maintenant partie des réseaux sociaux fédérés !",

Wyświetl plik

@ -18,6 +18,7 @@
"Post publicly" : "Poster publiquement",
"Post to followers" : "Poster aux abonnés",
"Post to recipients" : "Poster aux destinataires",
"Post unlisted" : "Post non répertorié",
"Direct" : "Direct",
"Post to mentioned users only" : "Poster aux utilisateurs mentionnées seulement",
"Unlisted" : "Non listé",
@ -32,7 +33,8 @@
"posts" : "messages",
"following" : "suivi",
"followers" : "abonnés",
"No accounts found" : "Aucun compte trouvé",
"No results found" : "Aucun résultat trouvé",
"There were no results for your search:" : "Aucun résultat n'a été trouvé pour votre recherche :",
"Searching for" : "Recherche de",
"No posts found" : "Aucun message trouvé",
"Posts from people you follow will show up here" : "Les messages des personnes que vous suivez apparaitront ici",
@ -42,6 +44,7 @@
"Posts from other people on this instance will show up here" : "Les messages d'autres personnes de cette instance apparaitront ici",
"No global posts found" : "Aucun message global trouvé",
"Posts from federated instances will show up here" : "Les messages des instances fédérées apparaitront ici",
"No posts found for this tag" : "Aucun post trouvé avec cette étiquette",
"User not found" : "Utilisateur non trouvé",
"Sorry, we could not find the account of {userId}" : "Désolé, impossible de trouver le compte {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud fait maintenant partie des réseaux sociaux fédérés !",

Wyświetl plik

@ -35,7 +35,8 @@ OC.L10N.register(
"posts" : "mensaxes",
"following" : "seguindo",
"followers" : "seguidoras",
"No accounts found" : "Non se atoparon contas",
"No results found" : "Non se atoparon resultados",
"There were no results for your search:" : "Non houbo resultados para a súa busca:",
"Searching for" : "Buscando",
"No posts found" : "Non se atoparon mensaxes",
"Posts from people you follow will show up here" : "As publicacións da xente que vostede segue amosaranse aquí",
@ -45,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "As publicacións doutras persoas desta instancia amosaranse aquí",
"No global posts found" : "Non hai mensaxes federadas",
"Posts from federated instances will show up here" : "As publicacións das instancias federadas amosaranse aquí",
"No posts found for this tag" : "Non se atoparon mensaxes por esta etiqueta",
"User not found" : "Non se atopou o usuario",
"Sorry, we could not find the account of {userId}" : " Mágoa, non foi posíbel atopar a conta de {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud pasa a formar parte das redes sociais federadas!",

Wyświetl plik

@ -33,7 +33,8 @@
"posts" : "mensaxes",
"following" : "seguindo",
"followers" : "seguidoras",
"No accounts found" : "Non se atoparon contas",
"No results found" : "Non se atoparon resultados",
"There were no results for your search:" : "Non houbo resultados para a súa busca:",
"Searching for" : "Buscando",
"No posts found" : "Non se atoparon mensaxes",
"Posts from people you follow will show up here" : "As publicacións da xente que vostede segue amosaranse aquí",
@ -43,6 +44,7 @@
"Posts from other people on this instance will show up here" : "As publicacións doutras persoas desta instancia amosaranse aquí",
"No global posts found" : "Non hai mensaxes federadas",
"Posts from federated instances will show up here" : "As publicacións das instancias federadas amosaranse aquí",
"No posts found for this tag" : "Non se atoparon mensaxes por esta etiqueta",
"User not found" : "Non se atopou o usuario",
"Sorry, we could not find the account of {userId}" : " Mágoa, non foi posíbel atopar a conta de {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud pasa a formar parte das redes sociais federadas!",

Wyświetl plik

@ -17,7 +17,6 @@ OC.L10N.register(
"Follow" : "Követ",
"posts" : "Küldések",
"following" : "további",
"No accounts found" : "Nincsenek ilyen felhasználói fiókok",
"Searching for" : "Keress",
"No posts found" : "Nem található hozzászólás",
"No direct messages found" : "Nem található közvetlen üzenet",

Wyświetl plik

@ -15,7 +15,6 @@
"Follow" : "Követ",
"posts" : "Küldések",
"following" : "további",
"No accounts found" : "Nincsenek ilyen felhasználói fiókok",
"Searching for" : "Keress",
"No posts found" : "Nem található hozzászólás",
"No direct messages found" : "Nem található közvetlen üzenet",

Wyświetl plik

@ -34,7 +34,6 @@ OC.L10N.register(
"posts" : "færslur",
"following" : "fylgir",
"followers" : "fylgjendur",
"No accounts found" : "Engir aðgangsreikningar fundust",
"Searching for" : "Leita að",
"No posts found" : "Engar færslur fundust",
"Posts from people you follow will show up here" : "Færslur frá fólki sem þú fylgir munu birtast hér",

Wyświetl plik

@ -32,7 +32,6 @@
"posts" : "færslur",
"following" : "fylgir",
"followers" : "fylgjendur",
"No accounts found" : "Engir aðgangsreikningar fundust",
"Searching for" : "Leita að",
"No posts found" : "Engar færslur fundust",
"Posts from people you follow will show up here" : "Færslur frá fólki sem þú fylgir munu birtast hér",

Wyświetl plik

@ -35,7 +35,8 @@ OC.L10N.register(
"posts" : "commenti",
"following" : "segui già",
"followers" : "seguaci",
"No accounts found" : "Nessun account trovato",
"No results found" : "Nessun risultato trovato",
"There were no results for your search:" : "Non ci sono stati risultati per la tua ricerca:",
"Searching for" : "Ricerca",
"No posts found" : "Nessun commento trovato",
"Posts from people you follow will show up here" : "Commenti da persone che segui saranno mostrati qui",
@ -45,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Commenti da altre persone su questa istanza saranno mostrati qui",
"No global posts found" : "Nessun commento locale trovato",
"Posts from federated instances will show up here" : "Commenti da istanze federate saranno mostrati qui",
"No posts found for this tag" : "Nessun commento trovato per questa etichetta",
"User not found" : "Utente non trovato",
"Sorry, we could not find the account of {userId}" : "Spiacenti, impossibile trovare l'account di {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud diventa parte delle reti sociali federate!",

Wyświetl plik

@ -33,7 +33,8 @@
"posts" : "commenti",
"following" : "segui già",
"followers" : "seguaci",
"No accounts found" : "Nessun account trovato",
"No results found" : "Nessun risultato trovato",
"There were no results for your search:" : "Non ci sono stati risultati per la tua ricerca:",
"Searching for" : "Ricerca",
"No posts found" : "Nessun commento trovato",
"Posts from people you follow will show up here" : "Commenti da persone che segui saranno mostrati qui",
@ -43,6 +44,7 @@
"Posts from other people on this instance will show up here" : "Commenti da altre persone su questa istanza saranno mostrati qui",
"No global posts found" : "Nessun commento locale trovato",
"Posts from federated instances will show up here" : "Commenti da istanze federate saranno mostrati qui",
"No posts found for this tag" : "Nessun commento trovato per questa etichetta",
"User not found" : "Utente non trovato",
"Sorry, we could not find the account of {userId}" : "Spiacenti, impossibile trovare l'account di {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud diventa parte delle reti sociali federate!",

Wyświetl plik

@ -35,7 +35,6 @@ OC.L10N.register(
"posts" : "投稿",
"following" : "フォロー中",
"followers" : "フォロワー",
"No accounts found" : "アカウントが見つかりません",
"Searching for" : "を探す",
"No posts found" : "投稿が見つかりません",
"Posts from people you follow will show up here" : "あなたがフォローしている人の投稿がここに表示されます",

Wyświetl plik

@ -33,7 +33,6 @@
"posts" : "投稿",
"following" : "フォロー中",
"followers" : "フォロワー",
"No accounts found" : "アカウントが見つかりません",
"Searching for" : "を探す",
"No posts found" : "投稿が見つかりません",
"Posts from people you follow will show up here" : "あなたがフォローしている人の投稿がここに表示されます",

Wyświetl plik

@ -27,7 +27,6 @@ OC.L10N.register(
"posts" : "ieraksts",
"following" : "seko",
"followers" : "sekotāji",
"No accounts found" : "Konti nav atrasti",
"Searching for" : "Meklēt",
"No posts found" : "Atrasta jauns ieraksts",
"No direct messages found" : "Tiešie ziņojumi nav atrasti",

Wyświetl plik

@ -25,7 +25,6 @@
"posts" : "ieraksts",
"following" : "seko",
"followers" : "sekotāji",
"No accounts found" : "Konti nav atrasti",
"Searching for" : "Meklēt",
"No posts found" : "Atrasta jauns ieraksts",
"No direct messages found" : "Tiešie ziņojumi nav atrasti",

Wyświetl plik

@ -35,7 +35,8 @@ OC.L10N.register(
"posts" : "berichten",
"following" : "volgend",
"followers" : "volgers",
"No accounts found" : "Geen accounts gevonden",
"No results found" : "Geen resultaten gevonden",
"There were no results for your search:" : "Geen resultaten voor je zoekterm:",
"Searching for" : "Zoeken naar",
"No posts found" : "Geen berichten gevonden",
"Posts from people you follow will show up here" : "Berichten van mensen die je volgt, worden hier getoond",
@ -45,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Berichten van anderen op deze server verschijnen hier",
"No global posts found" : "Geen globale berichten gevonden",
"Posts from federated instances will show up here" : "Berichten van gefedereerde servers worden hier getoond",
"No posts found for this tag" : "Geen berichten gevonden met deze tag",
"User not found" : "Gebruiker niet gevonden",
"Sorry, we could not find the account of {userId}" : "Sorry, we konden het account van {userId} niet vinden",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud wordt onderdeel van de gefedereerde sociale netwerken!",

Wyświetl plik

@ -33,7 +33,8 @@
"posts" : "berichten",
"following" : "volgend",
"followers" : "volgers",
"No accounts found" : "Geen accounts gevonden",
"No results found" : "Geen resultaten gevonden",
"There were no results for your search:" : "Geen resultaten voor je zoekterm:",
"Searching for" : "Zoeken naar",
"No posts found" : "Geen berichten gevonden",
"Posts from people you follow will show up here" : "Berichten van mensen die je volgt, worden hier getoond",
@ -43,6 +44,7 @@
"Posts from other people on this instance will show up here" : "Berichten van anderen op deze server verschijnen hier",
"No global posts found" : "Geen globale berichten gevonden",
"Posts from federated instances will show up here" : "Berichten van gefedereerde servers worden hier getoond",
"No posts found for this tag" : "Geen berichten gevonden met deze tag",
"User not found" : "Gebruiker niet gevonden",
"Sorry, we could not find the account of {userId}" : "Sorry, we konden het account van {userId} niet vinden",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud wordt onderdeel van de gefedereerde sociale netwerken!",

Wyświetl plik

@ -27,7 +27,6 @@ OC.L10N.register(
"posts" : "wpisy",
"following" : "śledzeni",
"followers" : "śledzący",
"No accounts found" : "Nie znaleziono kont",
"Searching for" : "Wyszukiwanie",
"No posts found" : "Nie znaleziono wpisów",
"Posts from people you follow will show up here" : "Tutaj będą pojawiać się wpisy osób które obserwujesz",

Wyświetl plik

@ -25,7 +25,6 @@
"posts" : "wpisy",
"following" : "śledzeni",
"followers" : "śledzący",
"No accounts found" : "Nie znaleziono kont",
"Searching for" : "Wyszukiwanie",
"No posts found" : "Nie znaleziono wpisów",
"Posts from people you follow will show up here" : "Tutaj będą pojawiać się wpisy osób które obserwujesz",

Wyświetl plik

@ -35,7 +35,8 @@ OC.L10N.register(
"posts" : "postagens",
"following" : "seguindo",
"followers" : "seguidores",
"No accounts found" : "Nenhuma conta encontrada",
"No results found" : "Nenhum resultado encontrado",
"There were no results for your search:" : "Não houve resultados para sua pesquisa:",
"Searching for" : "Procurando por",
"No posts found" : "Nenhuma postagem encontrada",
"Posts from people you follow will show up here" : "Postagens de pessoas que você segue aparecerão aqui",
@ -45,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Postagens de outras pessoas desta instância aparecerão aqui",
"No global posts found" : "Nenhuma postagem global encontrada",
"Posts from federated instances will show up here" : "Postagens de instâncias federadas aparecerão aqui",
"No posts found for this tag" : "Sem postagens para esta tag",
"User not found" : "Usuário não encontrado",
"Sorry, we could not find the account of {userId}" : "Não foi possível encontrar a conta de {userId}",
"Nextcloud becomes part of the federated social networks!" : "O Nextcloud torna-se parte de redes sociais federadas!",

Wyświetl plik

@ -33,7 +33,8 @@
"posts" : "postagens",
"following" : "seguindo",
"followers" : "seguidores",
"No accounts found" : "Nenhuma conta encontrada",
"No results found" : "Nenhum resultado encontrado",
"There were no results for your search:" : "Não houve resultados para sua pesquisa:",
"Searching for" : "Procurando por",
"No posts found" : "Nenhuma postagem encontrada",
"Posts from people you follow will show up here" : "Postagens de pessoas que você segue aparecerão aqui",
@ -43,6 +44,7 @@
"Posts from other people on this instance will show up here" : "Postagens de outras pessoas desta instância aparecerão aqui",
"No global posts found" : "Nenhuma postagem global encontrada",
"Posts from federated instances will show up here" : "Postagens de instâncias federadas aparecerão aqui",
"No posts found for this tag" : "Sem postagens para esta tag",
"User not found" : "Usuário não encontrado",
"Sorry, we could not find the account of {userId}" : "Não foi possível encontrar a conta de {userId}",
"Nextcloud becomes part of the federated social networks!" : "O Nextcloud torna-se parte de redes sociais federadas!",

Wyświetl plik

@ -28,7 +28,6 @@ OC.L10N.register(
"posts" : "publicações",
"following" : "a seguir",
"followers" : "seguidores",
"No accounts found" : "Nenhuma conta encontrada",
"Searching for" : "À procura de",
"No posts found" : "Nenhuma publicação encontrada",
"Posts from people you follow will show up here" : "Publicações de pessoas que segue serão apresentadas aqui",

Wyświetl plik

@ -26,7 +26,6 @@
"posts" : "publicações",
"following" : "a seguir",
"followers" : "seguidores",
"No accounts found" : "Nenhuma conta encontrada",
"Searching for" : "À procura de",
"No posts found" : "Nenhuma publicação encontrada",
"Posts from people you follow will show up here" : "Publicações de pessoas que segue serão apresentadas aqui",

Wyświetl plik

@ -6,6 +6,7 @@ OC.L10N.register(
".well-known/webfinger isn't properly set up!" : ".well-known/webfinger није добро подешен!",
"Open documentation" : "Отвори документацију",
"Social app setup" : "Поставке социјалне апликације",
"ActivityPub requires a fixed URL to make entries unique. Note that this can not be changed later without resetting the Social app." : "ActivityPub захтева фиксан URL да би сви уноси били јединствени. Ово се не може изменити накнадно, без да се ресетује цела апликација Социјалних мрежа.",
"ActivityPub URL base" : "ActivityPub URL основа",
"Finish setup" : "Заврши подешавање",
"The Social app needs to be set up by the server administrator." : "Социјалну апликацију мора да подеси администратор сервера.",
@ -32,7 +33,8 @@ OC.L10N.register(
"posts" : "објаве",
"following" : "прати",
"followers" : "пратиоца",
"No accounts found" : "Није пронађен ниједан налог",
"No results found" : "Нема пронађених резултата",
"There were no results for your search:" : "Нема резултата за претрагу:",
"Searching for" : "Тражим",
"No posts found" : "Нема објава",
"Posts from people you follow will show up here" : "Објаве од људи које пратите ће се појавити овде",
@ -42,6 +44,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Објаве од других људи са ове инстанце ће се појављивати овде",
"No global posts found" : "Нема јавних објава",
"Posts from federated instances will show up here" : "Објаве се федерисаних инстанци ће се појавити овде",
"No posts found for this tag" : "Нема нађених објава за ову ознаку",
"User not found" : "Корисник није нађен",
"Sorry, we could not find the account of {userId}" : "Нажалост, не можемо да нађемо налог корисника {userId}",
"Nextcloud becomes part of the federated social networks!" : "Некстклауд је постао део федерисаних социјалних мрежа!",

Wyświetl plik

@ -4,6 +4,7 @@
".well-known/webfinger isn't properly set up!" : ".well-known/webfinger није добро подешен!",
"Open documentation" : "Отвори документацију",
"Social app setup" : "Поставке социјалне апликације",
"ActivityPub requires a fixed URL to make entries unique. Note that this can not be changed later without resetting the Social app." : "ActivityPub захтева фиксан URL да би сви уноси били јединствени. Ово се не може изменити накнадно, без да се ресетује цела апликација Социјалних мрежа.",
"ActivityPub URL base" : "ActivityPub URL основа",
"Finish setup" : "Заврши подешавање",
"The Social app needs to be set up by the server administrator." : "Социјалну апликацију мора да подеси администратор сервера.",
@ -30,7 +31,8 @@
"posts" : "објаве",
"following" : "прати",
"followers" : "пратиоца",
"No accounts found" : "Није пронађен ниједан налог",
"No results found" : "Нема пронађених резултата",
"There were no results for your search:" : "Нема резултата за претрагу:",
"Searching for" : "Тражим",
"No posts found" : "Нема објава",
"Posts from people you follow will show up here" : "Објаве од људи које пратите ће се појавити овде",
@ -40,6 +42,7 @@
"Posts from other people on this instance will show up here" : "Објаве од других људи са ове инстанце ће се појављивати овде",
"No global posts found" : "Нема јавних објава",
"Posts from federated instances will show up here" : "Објаве се федерисаних инстанци ће се појавити овде",
"No posts found for this tag" : "Нема нађених објава за ову ознаку",
"User not found" : "Корисник није нађен",
"Sorry, we could not find the account of {userId}" : "Нажалост, не можемо да нађемо налог корисника {userId}",
"Nextcloud becomes part of the federated social networks!" : "Некстклауд је постао део федерисаних социјалних мрежа!",

Wyświetl plik

@ -33,7 +33,8 @@ OC.L10N.register(
"posts" : "Inlägg",
"following" : "följer",
"followers" : "följare",
"No accounts found" : "Inga konton hittades",
"No results found" : "Inga resultat funna",
"There were no results for your search:" : "Det var inga resultat för din sökning:",
"Searching for" : "Sök efter",
"No posts found" : "Inga inlägg hittades",
"Posts from people you follow will show up here" : "Inlägg från personer du följer visas här",
@ -43,6 +44,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Inlägg från andra personer i denna instans visas här",
"No global posts found" : "Inga globala inlägg hittade",
"Posts from federated instances will show up here" : "Inlägg från federerade instanser visas här",
"No posts found for this tag" : "Inga inlägg funna för denna tagg",
"User not found" : "Användaren hittades inte",
"Sorry, we could not find the account of {userId}" : "Tyvärr, vi kunde inte hitta kontot för {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud blir en del av federerade sociala nätverk!",

Wyświetl plik

@ -31,7 +31,8 @@
"posts" : "Inlägg",
"following" : "följer",
"followers" : "följare",
"No accounts found" : "Inga konton hittades",
"No results found" : "Inga resultat funna",
"There were no results for your search:" : "Det var inga resultat för din sökning:",
"Searching for" : "Sök efter",
"No posts found" : "Inga inlägg hittades",
"Posts from people you follow will show up here" : "Inlägg från personer du följer visas här",
@ -41,6 +42,7 @@
"Posts from other people on this instance will show up here" : "Inlägg från andra personer i denna instans visas här",
"No global posts found" : "Inga globala inlägg hittade",
"Posts from federated instances will show up here" : "Inlägg från federerade instanser visas här",
"No posts found for this tag" : "Inga inlägg funna för denna tagg",
"User not found" : "Användaren hittades inte",
"Sorry, we could not find the account of {userId}" : "Tyvärr, vi kunde inte hitta kontot för {userId}",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud blir en del av federerade sociala nätverk!",

Wyświetl plik

@ -35,7 +35,8 @@ OC.L10N.register(
"posts" : "ileti",
"following" : "takip edilenler",
"followers" : "takip edenler",
"No accounts found" : "Herhangi bir hesap bulunamadı",
"No results found" : "Herhangi bir sonuç bulunamadı",
"There were no results for your search:" : "Aramanızdan bir sonuç alınamadı:",
"Searching for" : "Şu ifade aranıyor",
"No posts found" : "Herhangi bir ileti bulunamadı",
"Posts from people you follow will show up here" : "Takip ettiğiniz kişilerin iletileri burada görüntülenir",
@ -45,6 +46,7 @@ OC.L10N.register(
"Posts from other people on this instance will show up here" : "Bu sunucuyu kullanan diğer kişilerin gönderdiği iletiler burada görüntülenir",
"No global posts found" : "Herhangi bir genel ileti bulunamadı",
"Posts from federated instances will show up here" : "Birleşmiş sunuculara gönderilmiş iletiler burada görüntülenir",
"No posts found for this tag" : "Bu etiketi taşıyan bir ileti bulunamadı",
"User not found" : "Kullanıcı bulunamadı",
"Sorry, we could not find the account of {userId}" : "Maalesef, {userId} hesabını bulamadık",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud birleşmiş sosyal ağların bir parçası haline geliyor!",

Wyświetl plik

@ -33,7 +33,8 @@
"posts" : "ileti",
"following" : "takip edilenler",
"followers" : "takip edenler",
"No accounts found" : "Herhangi bir hesap bulunamadı",
"No results found" : "Herhangi bir sonuç bulunamadı",
"There were no results for your search:" : "Aramanızdan bir sonuç alınamadı:",
"Searching for" : "Şu ifade aranıyor",
"No posts found" : "Herhangi bir ileti bulunamadı",
"Posts from people you follow will show up here" : "Takip ettiğiniz kişilerin iletileri burada görüntülenir",
@ -43,6 +44,7 @@
"Posts from other people on this instance will show up here" : "Bu sunucuyu kullanan diğer kişilerin gönderdiği iletiler burada görüntülenir",
"No global posts found" : "Herhangi bir genel ileti bulunamadı",
"Posts from federated instances will show up here" : "Birleşmiş sunuculara gönderilmiş iletiler burada görüntülenir",
"No posts found for this tag" : "Bu etiketi taşıyan bir ileti bulunamadı",
"User not found" : "Kullanıcı bulunamadı",
"Sorry, we could not find the account of {userId}" : "Maalesef, {userId} hesabını bulamadık",
"Nextcloud becomes part of the federated social networks!" : "Nextcloud birleşmiş sosyal ağların bir parçası haline geliyor!",

Wyświetl plik

@ -13,7 +13,6 @@ OC.L10N.register(
"Follow" : "关注",
"following" : "正在关注",
"followers" : "关注者",
"No accounts found" : "没有找到帐户",
"Searching for" : "搜索",
"No posts found" : "未找到文章",
"No local posts found" : "没有找到本地文章",

Wyświetl plik

@ -11,7 +11,6 @@
"Follow" : "关注",
"following" : "正在关注",
"followers" : "关注者",
"No accounts found" : "没有找到帐户",
"Searching for" : "搜索",
"No posts found" : "未找到文章",
"No local posts found" : "没有找到本地文章",

Wyświetl plik

@ -37,6 +37,7 @@ use OCA\Social\Service\AccountService;
use OCA\Social\Service\CacheActorService;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\DocumentService;
use OCA\Social\Service\HashtagService;
use OCA\Social\Service\MiscService;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -54,6 +55,9 @@ class CacheRefresh extends Base {
/** @var DocumentService */
private $documentService;
/** @var HashtagService */
private $hashtagService;
/** @var ConfigService */
private $configService;
@ -67,18 +71,21 @@ class CacheRefresh extends Base {
* @param AccountService $accountService
* @param CacheActorService $cacheActorService
* @param DocumentService $documentService
* @param HashtagService $hashtagService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
AccountService $accountService, CacheActorService $cacheActorService,
DocumentService $documentService, ConfigService $configService, MiscService $miscService
DocumentService $documentService, HashtagService $hashtagService,
ConfigService $configService, MiscService $miscService
) {
parent::__construct();
$this->accountService = $accountService;
$this->cacheActorService = $cacheActorService;
$this->documentService = $documentService;
$this->hashtagService = $hashtagService;
$this->configService = $configService;
$this->miscService = $miscService;
}
@ -116,6 +123,9 @@ class CacheRefresh extends Base {
$result = $this->documentService->manageCacheDocuments();
$output->writeLn($result . ' documents cached');
$result = $this->hashtagService->manageHashtags();
$output->writeLn($result . ' hashtags updated');
}

Wyświetl plik

@ -108,6 +108,10 @@ class NoteCreate extends Base {
'type', 'y', InputOption::VALUE_OPTIONAL,
'type: public (default), followers, unlisted, direct'
)
->addOption(
'hashtag', 'g', InputOption::VALUE_OPTIONAL,
'hashtag, without the leading #'
)
->addArgument('userid', InputArgument::REQUIRED, 'userId of the author')
->addArgument('content', InputArgument::REQUIRED, 'content of the post')
->setDescription('Create a new note');
@ -125,6 +129,7 @@ class NoteCreate extends Base {
$userId = $input->getArgument('userid');
$content = $input->getArgument('content');
$to = $input->getOption('to');
$hashtag = $input->getOption('hashtag');
$replyTo = $input->getOption('replyTo');
$type = $input->getOption('type');
@ -134,6 +139,7 @@ class NoteCreate extends Base {
$post->setType(($type === null) ? '' : $type);
$post->setReplyTo(($replyTo === null) ? '' : $replyTo);
$post->addTo(($to === null) ? '' : $to);
$post->setHashtags(($hashtag === null) ? [] : [$hashtag]);
$token = $this->postService->createPost($post, $activity);

Wyświetl plik

@ -48,6 +48,7 @@ use OCA\Social\Service\FollowService;
use OCA\Social\Service\MiscService;
use OCA\Social\Service\NoteService;
use OCA\Social\Service\PostService;
use OCA\Social\Service\SearchService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
@ -83,6 +84,9 @@ class LocalController extends Controller {
/** @var NoteService */
private $noteService;
/** @var SearchService */
private $searchService;
/** @var AccountService */
private $accountService;
@ -107,14 +111,15 @@ class LocalController extends Controller {
* @param FollowService $followService
* @param PostService $postService
* @param NoteService $noteService
* @param SearchService $searchService
* @param DocumentService $documentService
* @param MiscService $miscService
*/
public function __construct(
IRequest $request, $userId, AccountService $accountService,
CacheActorService $cacheActorService, FollowService $followService,
PostService $postService, NoteService $noteService, DocumentService $documentService,
MiscService $miscService
PostService $postService, NoteService $noteService, SearchService $searchService,
DocumentService $documentService, MiscService $miscService
) {
parent::__construct(Application::APP_NAME, $request);
@ -122,6 +127,7 @@ class LocalController extends Controller {
$this->cacheActorService = $cacheActorService;
$this->accountService = $accountService;
$this->noteService = $noteService;
$this->searchService = $searchService;
$this->postService = $postService;
$this->followService = $followService;
$this->documentService = $documentService;
@ -148,6 +154,7 @@ class LocalController extends Controller {
$post->setTo($this->getArray('to', $data, []));
$post->addTo($this->get('to', $data, ''));
$post->setType($this->get('type', $data, Stream::TYPE_PUBLIC));
$post->setHashtags($this->getArray('hashtags', $data, []));
/** @var ACore $activity */
$token = $this->postService->createPost($post, $activity);
@ -210,7 +217,7 @@ class LocalController extends Controller {
}
/**
/**
* @NoAdminRequired
*
* @param int $since
@ -294,6 +301,30 @@ class LocalController extends Controller {
}
}
/**
* Get timeline
*
* @NoAdminRequired
*
* @param string $hashtag
* @param int $since
* @param int $limit
*
* @return DataResponse
*/
public function streamTag(string $hashtag, int $since = 0, int $limit = 5): DataResponse {
try {
$this->initViewer(true);
$posts = $this->noteService->getStreamLocalTag($this->viewer, $hashtag, $since, $limit);
return $this->success($posts);
} catch (Exception $e) {
return $this->fail($e);
}
}
/**
* Get timeline
*
@ -581,6 +612,30 @@ class LocalController extends Controller {
}
/** // TODO - remove this tag
*
* @NoCSRFRequired
* @NoAdminRequired
*
* @param string $search
*
* @return DataResponse
* @throws Exception
*/
public function search(string $search): DataResponse {
$search = trim($search);
$this->initViewer();
$result = [
'accounts' => $this->searchService->searchAccounts($search),
'hashtags' => $this->searchService->searchHashtags($search),
'content' => $this->searchService->searchStreamContent($search)
];
return $this->success($result);
}
/**
* @NoAdminRequired
*

Wyświetl plik

@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* 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 the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Controller;
use daita\MySmallPhpTools\Exceptions\ArrayNotFoundException;
use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse;
use daita\MySmallPhpTools\Traits\TArrayTools;
use Exception;
use OCA\Social\AppInfo\Application;
use OCA\Social\Exceptions\RetrieveAccountFormatException;
use OCA\Social\Service\AccountService;
use OCA\Social\Service\CacheActorService;
use OCA\Social\Service\CurlService;
use OCA\Social\Service\MiscService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\IUserSession;
class OStatusController extends Controller {
use TNCDataResponse;
use TArrayTools;
/** @var CacheActorService */
private $cacheActorService;
/** @var AccountService */
private $accountService;
/** @var CurlService */
private $curlService;
/** @var MiscService */
private $miscService;
/** @var IUserManager */
private $userSession;
/**
* OStatusController constructor.
*
* @param IRequest $request
* @param CacheActorService $cacheActorService
* @param AccountService $accountService
* @param CurlService $curlService
* @param MiscService $miscService
*/
public function __construct(
IRequest $request, CacheActorService $cacheActorService, AccountService $accountService,
CurlService $curlService, MiscService $miscService, IUserSession $userSession
) {
parent::__construct(Application::APP_NAME, $request);
$this->cacheActorService = $cacheActorService;
$this->accountService = $accountService;
$this->curlService = $curlService;
$this->miscService = $miscService;
$this->userSession = $userSession;
}
/**
* @NoCSRFRequired
* @NoAdminRequired
*
* @param string $uri
*
* @return Response
*/
public function subscribe(string $uri): Response {
try {
$actor = $this->cacheActorService->getFromAccount($uri);
$user = $this->userSession->getUser();
if ($user === null) {
return $this->fail('Failed to retrieve current user');
}
return new TemplateResponse('social', 'ostatus', [
'serverData' => [
'account' => $actor->getAccount(),
'currentUser' => [
'uid' => $user->getUID(),
'displayName' => $user->getDisplayName(),
]
]
], 'guest');
} catch (Exception $e) {
return $this->fail($e);
}
}
/**
* @NoCSRFRequired
* @NoAdminRequired
* @PublicPage
*
* @param string $local
* @return Response
*/
public function followRemote(string $local): Response {
try {
$following = $this->accountService->getActor($local);
return new TemplateResponse('social', 'ostatus', [
'serverData' => [
'local' => $local,
'account' => $following->getAccount()
]
], 'guest');
} catch (\Exception $e) {
return $this->fail($e);
}
}
/**
* @NoCSRFRequired
* @NoAdminRequired
* @PublicPage
*
* @param string $local
* @param string $account
*
* @return Response
*/
public function getLink(string $local, string $account): Response {
try {
$following = $this->accountService->getActor($local);
$result = $this->curlService->webfingerAccount($account);
try {
$link = $this->extractArray(
'rel', 'http://ostatus.org/schema/1.0/subscribe',
$this->getArray('links', $result)
);
} catch (ArrayNotFoundException $e) {
throw new RetrieveAccountFormatException();
}
$template = $this->get('template', $link, '');
$url = str_replace('{uri}', $following->getAccount(), $template);
return $this->success(['url' => $url]);
} catch (Exception $e) {
return $this->fail($e);
}
}
}

Wyświetl plik

@ -38,6 +38,7 @@ use OCA\Social\Service\AccountService;
use OCA\Social\Service\CacheActorService;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\DocumentService;
use OCA\Social\Service\HashtagService;
use OCA\Social\Service\MiscService;
use OCP\AppFramework\QueryException;
@ -59,6 +60,9 @@ class Cache extends TimedJob {
/** @var DocumentService */
private $documentService;
/** @var HashtagService */
private $hashtagService;
/** @var ConfigService */
private $configService;
@ -86,6 +90,7 @@ class Cache extends TimedJob {
$this->accountService = $c->query(AccountService::class);
$this->cacheActorService = $c->query(CacheActorService::class);
$this->documentService = $c->query(DocumentService::class);
$this->hashtagService = $c->query(HashtagService::class);
$this->configService = $c->query(ConfigService::class);
$this->miscService = $c->query(MiscService::class);
@ -113,6 +118,11 @@ class Cache extends TimedJob {
$this->documentService->manageCacheDocuments();
} catch (Exception $e) {
}
try {
$this->hashtagService->manageHashtags();
} catch (Exception $e) {
}
}

Wyświetl plik

@ -250,6 +250,7 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
$this->searchInAccount($qb, $search);
$this->leftJoinCacheDocuments($qb, 'icon_id');
$this->leftJoinDetails($qb);
$this->limitResults($qb, 25);
$accounts = [];
$cursor = $qb->execute();

Wyświetl plik

@ -58,6 +58,7 @@ class CoreRequestBuilder {
const TABLE_SERVER_ACTORS = 'social_server_actors';
const TABLE_SERVER_NOTES = 'social_server_notes';
const TABLE_SERVER_HASHTAGS = 'social_server_hashtags';
const TABLE_SERVER_FOLLOWS = 'social_server_follows';
const TABLE_CACHE_ACTORS = 'social_cache_actors';
@ -65,7 +66,6 @@ class CoreRequestBuilder {
const TABLE_QUEUE_STREAM = 'social_queue_stream';
/** @var IDBConnection */
protected $dbConnection;
@ -202,6 +202,39 @@ class CoreRequestBuilder {
$this->limitToDBField($qb, 'token', $token);
}
/**
* Limit the results to a given number
*
* @param IQueryBuilder $qb
* @param int $limit
*/
protected function limitResults(IQueryBuilder $qb, int $limit) {
$qb->setMaxResults($limit);
}
/**
* Limit the request to the ActorId
*
* @param IQueryBuilder $qb
* @param string $hashtag
*/
protected function limitToHashtag(IQueryBuilder &$qb, string $hashtag) {
$this->limitToDBField($qb, 'hashtag', $hashtag, false);
}
/**
* Limit the request to the ActorId
*
* @param IQueryBuilder $qb
* @param string $hashtag
*/
protected function searchInHashtag(IQueryBuilder &$qb, string $hashtag) {
$dbConn = $this->dbConnection;
$this->searchInDBField($qb, 'hashtag', '%' . $dbConn->escapeLikeParameter($hashtag) . '%');
}
/**
* Limit the request to the ActorId
@ -496,6 +529,27 @@ class CoreRequestBuilder {
}
/**
* @param IQueryBuilder $qb
* @param int $timestamp
* @param string $field
*/
protected function limitToSince(IQueryBuilder $qb, int $timestamp, string $field) {
$dTime = new \DateTime();
$dTime->setTimestamp($timestamp);
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? $this->defaultSelectAlias . '.' : '';
$field = $pf . $field;
$orX = $expr->orX();
$orX->add($expr->gte($field, $qb->createNamedParameter($dTime, IQueryBuilder::PARAM_DATE)));
$qb->andWhere($orX);
}
/**
* @param IQueryBuilder $qb
* @param string $field

Wyświetl plik

@ -0,0 +1,145 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* 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 the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Db;
use daita\MySmallPhpTools\Traits\TArrayTools;
use DateTime;
use OCA\Social\Exceptions\FollowDoesNotExistException;
use OCA\Social\Exceptions\HashtagDoesNotExistException;
use OCA\Social\Model\ActivityPub\Activity\Follow;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class HashtagsRequest
*
* @package OCA\Social\Db
*/
class HashtagsRequest extends HashtagsRequestBuilder {
use TArrayTools;
/**
* Insert a new Hashtag.
*
* @param string $hashtag
* @param array $trend
*/
public function save(string $hashtag, array $trend) {
$qb = $this->getHashtagsInsertSql();
$qb->setValue('hashtag', $qb->createNamedParameter($hashtag))
->setValue('trend', $qb->createNamedParameter(json_encode($trend)));
$qb->execute();
}
/**
* Insert a new Hashtag.
*
* @param string $hashtag
* @param array $trend
*/
public function update(string $hashtag, array $trend) {
$qb = $this->getHashtagsUpdateSql();
$qb->set('trend', $qb->createNamedParameter(json_encode($trend)));
$this->limitToHashtag($qb, $hashtag);
$qb->execute();
}
/**
* @return array
*/
public function getAll(): array {
$qb = $this->getHashtagsSelectSql();
$hashtags = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$hashtags[] = $this->parseHashtagsSelectSql($data);
}
$cursor->closeCursor();
return $hashtags;
}
/**
* @param string $hashtag
*
* @return array
* @throws HashtagDoesNotExistException
*/
public function getHashtag(string $hashtag): array {
$qb = $this->getHashtagsSelectSql();
$this->limitToHashtag($qb, $hashtag);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new HashtagDoesNotExistException();
}
return $this->parseHashtagsSelectSql($data);
}
/**
* @param string $hashtag
*
* @return array
*/
public function searchHashtags(string $hashtag): array {
$qb = $this->getHashtagsSelectSql();
$this->searchInHashtag($qb, $hashtag);
$this->limitResults($qb, 25);
$hashtags = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$hashtags[] = $this->parseHashtagsSelectSql($data);
}
$cursor->closeCursor();
return $hashtags;
}
}

Wyświetl plik

@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* 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 the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Db;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Model\ActivityPub\Activity\Follow;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class HashtagsRequestBuilder
*
* @package OCA\Social\Db
*/
class HashtagsRequestBuilder extends CoreRequestBuilder {
use TArrayTools;
/**
* Base of the Sql Insert request
*
* @return IQueryBuilder
*/
protected function getHashtagsInsertSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_SERVER_HASHTAGS);
return $qb;
}
/**
* Base of the Sql Update request
*
* @return IQueryBuilder
*/
protected function getHashtagsUpdateSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_SERVER_HASHTAGS);
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
protected function getHashtagsSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select('h.hashtag', 'h.trend')
->from(self::TABLE_SERVER_HASHTAGS, 'h');
$this->defaultSelectAlias = 'h';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getHashtagsDeleteSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_SERVER_HASHTAGS);
return $qb;
}
/**
* @param array $data
*
* @return array
*/
protected function parseHashtagsSelectSql($data): array {
return [
'hashtag' => $this->get('hashtag', $data, ''),
'trend' => $this->getArray('trend', $data, [])
];
}
}

Wyświetl plik

@ -94,6 +94,7 @@ class NotesRequest extends NotesRequestBuilder {
)
->setValue('content', $qb->createNamedParameter($note->getContent()))
->setValue('summary', $qb->createNamedParameter($note->getSummary()))
->setValue('hashtags', $qb->createNamedParameter(json_encode($note->getHashtags())))
->setValue('published', $qb->createNamedParameter($note->getPublished()))
->setValue(
'published_time', $qb->createNamedParameter($dTime, IQueryBuilder::PARAM_DATE)
@ -355,6 +356,64 @@ class NotesRequest extends NotesRequestBuilder {
}
/**
* Should returns:
* - All public post related to a tag (not yet)
* - direct message related to a tag (not yet)
* - message to followers related to a tag (not yet)
*
* @param Person $actor
* @param string $hashtag
* @param int $since
* @param int $limit
*
* @return array
*/
public function getStreamTag(Person $actor, string $hashtag, int $since = 0, int $limit = 5
): array {
$qb = $this->getNotesSelectSql();
$on = $this->exprJoinFollowing($qb, $actor);
$on->add($this->exprLimitToRecipient($qb, ACore::CONTEXT_PUBLIC, false));
$on->add($this->exprLimitToRecipient($qb, $actor->getId(), true));
$qb->join($this->defaultSelectAlias, CoreRequestBuilder::TABLE_SERVER_FOLLOWS, 'f', $on);
$qb->andWhere($this->exprValueWithinJsonFormat($qb, 'hashtags', '' . $hashtag));
$this->limitPaginate($qb, $since, $limit);
$this->leftJoinCacheActors($qb, 'attributed_to');
$notes = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$notes[] = $this->parseNotesSelectSql($data);
}
$cursor->closeCursor();
return $notes;
}
/**
* @param int $since
*
* @return Note[]
*/
public function getNotesSince(int $since): array {
$qb = $this->getNotesSelectSql();
$this->limitToSince($qb, $since, 'published_time');
$notes = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$notes[] = $this->parseNotesSelectSql($data);
}
$cursor->closeCursor();
return $notes;
}
/**
* @param string $id
*/
@ -365,5 +424,16 @@ class NotesRequest extends NotesRequestBuilder {
$qb->execute();
}
/**
* @param string $actorId
*/
public function deleteByAuthor(string $actorId) {
$qb = $this->getNotesDeleteSql();
$this->limitToAttributedTo($qb, $actorId);
$qb->execute();
}
}

Wyświetl plik

@ -80,8 +80,9 @@ class NotesRequestBuilder extends CoreRequestBuilder {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
'sn.id', 'sn.type', 'sn.to', 'sn.to_array', 'sn.cc', 'sn.bcc', 'sn.content',
$qb->selectDistinct('sn.id')
->addSelect(
'sn.type', 'sn.to', 'sn.to_array', 'sn.cc', 'sn.bcc', 'sn.content',
'sn.summary', 'sn.published', 'sn.published_time', 'sn.cache', 'sn.object_id',
'sn.attributed_to', 'sn.in_reply_to', 'sn.source', 'sn.local', 'sn.instances',
'sn.creation'
@ -132,6 +133,19 @@ class NotesRequestBuilder extends CoreRequestBuilder {
return;
}
$on = $this->exprJoinFollowing($qb, $actor);
$qb->join($this->defaultSelectAlias, CoreRequestBuilder::TABLE_SERVER_FOLLOWS, 'f', $on);
}
/**
* @param IQueryBuilder $qb
* @param Person $actor
*
* @return ICompositeExpression
*/
protected function exprJoinFollowing(IQueryBuilder $qb, Person $actor) {
$expr = $qb->expr();
$func = $qb->func();
$pf = $this->defaultSelectAlias . '.';
@ -153,7 +167,7 @@ class NotesRequestBuilder extends CoreRequestBuilder {
$crossFollows->add($this->exprLimitToDBFieldInt($qb, 'accepted', 1, 'f'));
$on->add($crossFollows);
$qb->join($this->defaultSelectAlias, CoreRequestBuilder::TABLE_SERVER_FOLLOWS, 'f', $on);
return $on;
}

Wyświetl plik

@ -0,0 +1,8 @@
<?php
namespace OCA\Social\Exceptions;
class HashtagDoesNotExistException extends \Exception {
}

Wyświetl plik

@ -72,11 +72,13 @@ class DeleteInterface implements IActivityPubInterface {
$types = ['Note', 'Person'];
foreach ($types as $type) {
try {
$item->checkOrigin($item->getObjectId());
$interface = AP::$activityPub->getInterfaceForItem($type);
$object = $interface->getItemById($item->getObjectId());
$interface->delete($object);
return;
} catch (InvalidOriginException $e) {
} catch (ItemNotFoundException $e) {
} catch (ItemUnknownException $e) {
}
@ -88,8 +90,10 @@ class DeleteInterface implements IActivityPubInterface {
$object = $item->getObject();
try {
$item->checkOrigin($object->getId());
$interface = AP::$activityPub->getInterfaceForItem($object);
$interface->delete($object);
} catch (InvalidOriginException $e) {
} catch (ItemUnknownException $e) {
}
}

Wyświetl plik

@ -33,11 +33,13 @@ namespace OCA\Social\Interfaces\Actor;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCA\Social\Db\CacheActorsRequest;
use OCA\Social\Db\NotesRequest;
use OCA\Social\Exceptions\CacheActorDoesNotExistException;
use OCA\Social\Exceptions\InvalidOriginException;
use OCA\Social\Exceptions\ItemNotFoundException;
use OCA\Social\Interfaces\IActivityPubInterface;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Activity\Delete;
use OCA\Social\Model\ActivityPub\Activity\Update;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Service\ActorService;
@ -59,6 +61,9 @@ class PersonInterface implements IActivityPubInterface {
/** @var CacheActorsRequest */
private $cacheActorsRequest;
/** @var NotesRequest */
private $notesRequest;
/** @var ActorService */
private $actorService;
@ -73,15 +78,17 @@ class PersonInterface implements IActivityPubInterface {
* UndoService constructor.
*
* @param CacheActorsRequest $cacheActorsRequest
* @param NotesRequest $notesRequest
* @param ActorService $actorService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
CacheActorsRequest $cacheActorsRequest, ActorService $actorService,
ConfigService $configService, MiscService $miscService
CacheActorsRequest $cacheActorsRequest, NotesRequest $notesRequest,
ActorService $actorService, ConfigService $configService, MiscService $miscService
) {
$this->cacheActorsRequest = $cacheActorsRequest;
$this->notesRequest = $notesRequest;
$this->actorService = $actorService;
$this->configService = $configService;
$this->miscService = $miscService;
@ -141,36 +148,40 @@ class PersonInterface implements IActivityPubInterface {
*/
public function activity(Acore $activity, ACore $item) {
/** @var Person $item */
$activity->checkOrigin($item->getId());
if ($activity->getType() === Update::TYPE) {
$activity->checkOrigin($item->getId());
$item->setCreation($activity->getOriginCreationTime());
try {
$current = $this->cacheActorsRequest->getFromId($item->getId());
if ($current->getCreation() < $activity->getOriginCreationTime()) {
$this->cacheActorsRequest->update($item);
}
} catch (CacheActorDoesNotExistException $e) {
$this->cacheActorsRequest->save($item);
}
$this->updateActor($item, $activity);
}
}
/**
* @param ACore $item
*
* @throws InvalidOriginException
*/
public function delete(ACore $item) {
$item->checkOrigin(($item->getId()));
/** @var Person $item */
$this->cacheActorsRequest->deleteFromId($item->getId());
$this->notesRequest->deleteByAuthor($item->getId());
}
/**
* @param Person $actor
* @param ACore $activity
*/
private function updateActor(Person $actor, ACore $activity) {
$actor->setCreation($activity->getOriginCreationTime());
try {
$current = $this->cacheActorsRequest->getFromId($actor->getId());
if ($current->getCreation() < $activity->getOriginCreationTime()) {
$this->cacheActorsRequest->update($actor);
}
} catch (CacheActorDoesNotExistException $e) {
$this->cacheActorsRequest->save($actor);
}
}
}

Wyświetl plik

@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* 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 the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Migration;
use Closure;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Types\Type;
use OCA\Social\Db\CoreRequestBuilder;
use OCP\DB\ISchemaWrapper;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
/**
* Class Version0002Date20190201000001
*
* @package OCA\Social\Migration
*/
class Version0002Date20190201000001 extends SimpleMigrationStep {
/** @var IDBConnection */
private $connection;
/**
* @param IDBConnection $connection
*/
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*
* @return ISchemaWrapper
* @throws SchemaException
* @throws DBALException
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options
): ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if (!$schema->hasTable(CoreRequestBuilder::TABLE_SERVER_HASHTAGS)) {
$table = $schema->createTable(CoreRequestBuilder::TABLE_SERVER_HASHTAGS);
$table->addColumn('hashtag', 'string', ['notnull' => false, 'length' => 63]);
$table->addColumn(
'trend', 'string', ['notnull' => false, 'length' => 500]
);
$table->setPrimaryKey(['hashtag']);
}
$table = $schema->getTable(CoreRequestBuilder::TABLE_SERVER_NOTES);
if (!$table->hasColumn('hashtags')) {
$table->addColumn(
'hashtags', Type::STRING, ['notnull' => false, 'default' => '[]', 'length' => 1000]
);
}
return $schema;
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
}
}

Wyświetl plik

@ -61,6 +61,7 @@ class ACore extends Item implements JsonSerializable {
const AS_USERNAME = 5;
const AS_ACCOUNT = 6;
const AS_STRING = 7;
const AS_TAGS = 10;
/** @var null Item */
@ -473,7 +474,11 @@ class ACore extends Item implements JsonSerializable {
$result = [];
foreach ($values as $value) {
try {
$result[] = $this->validateEntryString($as, $value);
if (is_array($value)) {
$result[] = $this->validateEntryArray($as, $value);
} else {
$result[] = $this->validateEntryString($as, $value);
}
} catch (InvalidResourceEntryException $e) {
}
}
@ -485,13 +490,14 @@ class ACore extends Item implements JsonSerializable {
/**
* // TODO - better checks
*
* @param $as
* @param $value
* @param int $as
* @param string $value
* @param bool $exception
*
* @return string
* @throws InvalidResourceEntryException
*/
public function validateEntryString(int $as, string $value): string {
public function validateEntryString(int $as, string $value, bool $exception = true): string {
switch ($as) {
case self::AS_ID:
if (parse_url($value) !== false) {
@ -526,12 +532,41 @@ class ACore extends Item implements JsonSerializable {
$value = strip_tags($value);
return $value;
default:
break;
}
throw new InvalidResourceEntryException($as . ' ' . $value);
if ($exception) {
throw new InvalidResourceEntryException($as . ' ' . $value);
} else {
return '';
}
}
/**
* @param int $as
* @param array $values
*
* @return array
* @throws InvalidResourceEntryException
*/
public function validateEntryArray(int $as, array $values): array {
switch ($as) {
case self::AS_TAGS:
return [
'type' => $this->validateEntryString(
self::AS_TYPE, $this->get('type', $values, ''), false
),
'href' => $this->validateEntryString(
self::AS_URL, $this->get('href', $values, ''), false
),
'name' => $this->validateEntryString(
self::AS_STRING, $this->get('name', $values, ''), false
)
];
}
throw new InvalidResourceEntryException($as . ' ' . json_encode($values));
}
@ -548,6 +583,7 @@ class ACore extends Item implements JsonSerializable {
$this->setPublished($this->validate(self::AS_DATE, 'published', $data, ''));
$this->setActorId($this->validate(self::AS_ID, 'actor', $data, ''));
$this->setObjectId($this->validate(self::AS_ID, 'object', $data, ''));
$this->setTags($this->validateArray(self::AS_TAGS, 'tag', $data, []));
}

Wyświetl plik

@ -30,6 +30,7 @@ declare(strict_types=1);
namespace OCA\Social\Model\ActivityPub;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\InstancePath;
@ -37,6 +38,9 @@ use OCA\Social\Model\InstancePath;
class Item {
use TArrayTools;
/** @var string */
private $urlSocial = '';
@ -502,10 +506,23 @@ class Item {
}
/**
* @param string $type
*
* @return array
*/
public function getTags(): array {
return $this->tags;
public function getTags(string $type = ''): array {
if ($type === '') {
return $this->tags;
}
$result = [];
foreach ($this->tags as $tag) {
if ($this->get('type', $tag, '') === $type) {
$result[] = $tag;
}
}
return $result;
}
/**

Wyświetl plik

@ -41,6 +41,10 @@ class Note extends Stream implements JsonSerializable {
const TYPE = 'Note';
/** @var array */
private $hashtags = [];
/**
* Note constructor.
*
@ -53,6 +57,42 @@ class Note extends Stream implements JsonSerializable {
}
/**
* @return array
*/
public function getHashtags(): array {
return $this->hashtags;
}
/**
* @param array $hashtags
*
* @return Note
*/
public function setHashtags(array $hashtags): Note {
$this->hashtags = $hashtags;
return $this;
}
/**
*
*/
public function fillHashtags() {
$tags = $this->getTags('Hashtag');
$hashtags = [];
foreach ($tags as $tag) {
$hashtag = $tag['name'];
if (substr($hashtag, 0, 1) === '#') {
$hashtag = substr($hashtag, 1);
}
$hashtags[] = $hashtag;
}
$this->setHashtags($hashtags);
}
/**
* @param array $data
*/
@ -66,6 +106,8 @@ class Note extends Stream implements JsonSerializable {
*/
public function importFromDatabase(array $data) {
parent::importFromDatabase($data);
$this->setHashtags($this->getArray('hashtags', $data, []));
}
@ -73,7 +115,13 @@ class Note extends Stream implements JsonSerializable {
* @return array
*/
public function jsonSerialize(): array {
return parent::jsonSerialize();
$result = parent::jsonSerialize();
if ($this->isCompleteDetails()) {
$result['hashtags'] = $this->getHashtags();
}
return $result;
}
}

Wyświetl plik

@ -61,6 +61,9 @@ class Post implements JsonSerializable {
/** @var string */
private $type = '';
/** @var array */
private $hashtags = [];
/**
* Post constructor.
@ -148,6 +151,25 @@ class Post implements JsonSerializable {
}
/**
* @return array
*/
public function getHashtags(): array {
return $this->hashtags;
}
/**
* @param array $hashtags
*
* @return Post
*/
public function setHashtags(array $hashtags): Post {
$this->hashtags = $hashtags;
return $this;
}
/**
* @return string
*/

Wyświetl plik

@ -161,6 +161,7 @@ class AccountService {
* @throws NoUserException
* @throws SocialAppConfigException
* @throws UrlCloudException
* @throws ItemUnknownException
*/
public function getActorFromUserId(string $userId, bool $create = false): Person {
$this->miscService->confirmUserId($userId);

Wyświetl plik

@ -33,6 +33,7 @@ namespace OCA\Social\Service;
use daita\MySmallPhpTools\Exceptions\MalformedArrayException;
use daita\MySmallPhpTools\Model\Request;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use Exception;
use OCA\Social\Exceptions\CacheContentException;
use OCA\Social\Exceptions\CacheContentMimeTypeException;
@ -52,6 +53,7 @@ class CacheDocumentService {
use TArrayTools;
use TStringTools;
/** @var IAppData */
@ -103,11 +105,7 @@ class CacheDocumentService {
*/
public function saveRemoteFileToCache(string $url, &$mime = '') {
$filename = sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff), mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
$filename = $this->uuid();
// creating a path aa/bb/cc/dd/ from the filename aabbccdd-0123-[...]
$path = chunk_split(substr($filename, 0, 8), 2, '/');

Wyświetl plik

@ -90,21 +90,15 @@ class CurlService {
/**
* @param string $account
*
* @return Person
* @throws InvalidOriginException
* @return array
* @throws InvalidResourceException
* @throws MalformedArrayException
* @throws RedundancyLimitException
* @throws RequestContentException
* @throws RetrieveAccountFormatException
* @throws RequestNetworkException
* @throws RequestResultSizeException
* @throws RequestServerException
* @throws SocialAppConfigException
* @throws ItemUnknownException
* @throws RequestResultNotJsonException
*/
public function retrieveAccount(string $account): Person {
public function webfingerAccount(string $account): array {
$account = $this->withoutBeginAt($account);
// we consider an account is like an email
@ -122,6 +116,29 @@ class CurlService {
$request->setAddress($host);
$result = $this->request($request);
return $result;
}
/**
* @param string $account
*
* @return Person
* @throws InvalidOriginException
* @throws InvalidResourceException
* @throws MalformedArrayException
* @throws RedundancyLimitException
* @throws RequestContentException
* @throws RetrieveAccountFormatException
* @throws RequestNetworkException
* @throws RequestResultSizeException
* @throws RequestServerException
* @throws SocialAppConfigException
* @throws ItemUnknownException
*/
public function retrieveAccount(string $account): Person {
$result = $this->webfingerAccount($account);
try {
$link = $this->extractArray('rel', 'self', $this->getArray('links', $result));
} catch (ArrayNotFoundException $e) {

Wyświetl plik

@ -0,0 +1,233 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* 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 the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Service;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCA\Social\Db\HashtagsRequest;
use OCA\Social\Db\NotesRequest;
use OCA\Social\Exceptions\HashtagDoesNotExistException;
class HashtagService {
const TREND_1H = 3600;
const TREND_12H = 43200;
const TREND_1D = 86400;
const TREND_3D = 259200;
const TREND_10D = 864000;
use TArrayTools;
/** @var HashtagsRequest */
private $hashtagsRequest;
/** @var NotesRequest */
private $notesRequest;
/** @var ConfigService */
private $configService;
/** @var MiscService */
private $miscService;
/**
* ImportService constructor.
*
* @param HashtagsRequest $hashtagsRequest
* @param NotesRequest $notesRequest
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
HashtagsRequest $hashtagsRequest, NotesRequest $notesRequest, ConfigService $configService,
MiscService $miscService
) {
$this->hashtagsRequest = $hashtagsRequest;
$this->notesRequest = $notesRequest;
$this->configService = $configService;
$this->miscService = $miscService;
}
/*
* note: trend is:
* [
* '1h' => x,
* '12h' => x,
* '1d' => x,
* '3d' => x,
* '10d' => x
* ]
*/
/**
*
*/
public function manageHashtags(): int {
$current = $this->hashtagsRequest->getAll();
$time = time();
$hashtags = [
'1h' => $this->getTrendSince($time - self::TREND_1H),
'12h' => $this->getTrendSince($time - self::TREND_12H),
'1d' => $this->getTrendSince($time - self::TREND_1D),
'3d' => $this->getTrendSince($time - self::TREND_3D),
'10d' => $this->getTrendSince($time - self::TREND_10D)
];
$count = 0;
$formatted = $this->formatTrend($hashtags);
foreach ($formatted as $hashtag => $trend) {
$count++;
try {
$this->getFromList($current, $hashtag);
$this->hashtagsRequest->update($hashtag, $trend);
} catch (HashtagDoesNotExistException $e) {
$this->hashtagsRequest->save($hashtag, $trend);
}
}
return $count;
}
/**
* @param string $hashtag
*
* @return array
* @throws HashtagDoesNotExistException
*/
public function getHashtag(string $hashtag): array {
if (substr($hashtag, 0, 1) !== '#') {
$hashtag = '#' . $hashtag;
}
return $this->hashtagsRequest->getHashtag($hashtag);
}
/**
* @param string $hashtag
*
* @return array
*/
public function searchHashtags(string $hashtag): array {
return $this->hashtagsRequest->searchHashtags($hashtag);
}
/**
* @param int $timestamp
*
* @return array
*/
private function getTrendSince(int $timestamp): array {
$result = [];
$notes = $this->notesRequest->getNotesSince($timestamp);
foreach ($notes as $note) {
foreach ($note->getHashtags() as $hashtag) {
if (array_key_exists($hashtag, $result)) {
$result[$hashtag]++;
} else {
$result[$hashtag] = 1;
}
}
}
return $result;
}
/**
* @param array $hashtags
*
* @return array
*/
private function formatTrend(array $hashtags): array {
$trends = [];
foreach (end($hashtags) as $hashtag => $count) {
$trends[$hashtag] = [];
}
$all = array_keys($trends);
$periods = array_keys($hashtags);
foreach ($all as $hashtag) {
foreach ($periods as $period) {
$count = $this->countFromList($hashtags[$period], $hashtag);
$trends[$hashtag][$period] = $count;
}
}
return $trends;
}
/**
* @param array $list
* @param string $hashtag
*
* @return int
*/
private function countFromList(array $list, string $hashtag): int {
foreach ($list as $key => $count) {
if ($key === $hashtag) {
return $count;
}
}
return 0;
}
/**
* @param array $list
* @param string $hashtag
*
* @return array
* @throws HashtagDoesNotExistException
*/
private function getFromList(array $list, string $hashtag): array {
foreach ($list as $item) {
if ($this->get('hashtag', $item, '') === $hashtag) {
return $item;
}
}
throw new HashtagDoesNotExistException();
}
}

Wyświetl plik

@ -212,7 +212,8 @@ class NoteService {
$note->addTag(
[
'type' => 'Mention',
'href' => $actor->getId()
'href' => $actor->getId(),
'name' => '@' . $account
]
);
@ -220,22 +221,50 @@ class NoteService {
}
/**
* @param Note $note
* @param string $hashtag
*/
public function addHashtag(Note $note, string $hashtag) {
try {
$note->addTag(
[
'type' => 'Hashtag',
'href' => $this->configService->getCloudAddress() . '/tag/' . strtolower(
$hashtag
),
'name' => '#' . $hashtag
]
);
} catch (SocialAppConfigException $e) {
}
}
/**
* @param Note $note
* @param string $type
* @param array $accounts
*/
public function addRecipients(Note $note, string $type, array $accounts) {
if ($accounts === []) {
return;
}
foreach ($accounts as $account) {
$this->addRecipient($note, $type, $account);
}
}
/**
* @param Note $note
* @param array $hashtags
*/
public function addHashtags(Note $note, array $hashtags) {
$note->setHashtags($hashtags);
foreach ($hashtags as $hashtag) {
$this->addHashtag($note, $hashtag);
}
}
/**
* @param Note $note
* @param string $replyTo
@ -355,6 +384,19 @@ class NoteService {
}
/**
* @param Person $actor
* @param string $hashtag
* @param int $since
* @param int $limit
*
* @return Note[]
*/
public function getStreamLocalTag(Person $actor, string $hashtag, int $since = 0, int $limit = 5): array {
return $this->notesRequest->getStreamTag($actor, $hashtag, $since, $limit);
}
/**
* @param int $since
* @param int $limit

Wyświetl plik

@ -104,6 +104,7 @@ class PostService {
$this->noteService->replyTo($note, $post->getReplyTo());
$this->noteService->addRecipients($note, $post->getType(), $post->getTo());
$this->noteService->addHashtags($note, $post->getHashtags());
$result = $this->activityService->createActivity($post->getActor(), $note, $activity);
$this->accountService->cacheLocalActorDetailCount($post->getActor());

Wyświetl plik

@ -0,0 +1,190 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* 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 the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Service;
use daita\MySmallPhpTools\Traits\TArrayTools;
use Exception;
class SearchService {
use TArrayTools;
const SEARCH_ACCOUNTS = 1;
const SEARCH_HASHTAGS = 2;
const SEARCH_CONTENT = 4;
const SEARCH_ALL = 7;
/** @var CacheActorService */
private $cacheActorService;
/** @var HashtagService */
private $hashtagService;
/** @var ConfigService */
private $configService;
/** @var MiscService */
private $miscService;
/**
* ImportService constructor.
*
* @param CacheActorService $cacheActorService
* @param HashtagService $hashtagService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
CacheActorService $cacheActorService, HashtagService $hashtagService,
ConfigService $configService, MiscService $miscService
) {
$this->cacheActorService = $cacheActorService;
$this->hashtagService = $hashtagService;
$this->configService = $configService;
$this->miscService = $miscService;
}
/**
* @param string $search
*
* @return array
*/
public function searchAccounts(string $search): array {
$result = [
'exact' => null,
'result' => []
];
$type = $this->getTypeFromSearch($search);
if ($search === '' || !$type & self::SEARCH_ACCOUNTS) {
return $result;
}
if (substr($search, 0, 1) === '@') {
$search = substr($search, 1);
}
try {
$exact = $this->cacheActorService->getFromAccount($search);
$exact->setCompleteDetails(true);
$result['exact'] = $exact;
} catch (Exception $e) {
}
try {
$accounts = $this->cacheActorService->searchCachedAccounts($search);
$result['result'] = $accounts;
} catch (Exception $e) {
}
return $result;
}
/**
* @param string $search
*
* @return array
*/
public function searchHashtags(string $search): array {
$result = [
'exact' => null,
'result' => []
];
$type = $this->getTypeFromSearch($search);
if ($search === '' || !$type & self::SEARCH_HASHTAGS) {
return $result;
}
try {
$exact = $this->hashtagService->getHashtag($search);
$result['exact'] = $exact;
} catch (Exception $e) {
}
try {
$hashtags = $this->hashtagService->searchHashtags($search);
$result['result'] = $hashtags;
} catch (Exception $e) {
}
return $result;
}
/**
* @param string $search
*
* @return array
*/
public function searchStreamContent(string $search): array {
$result = [];
$type = $this->getTypeFromSearch($search);
if ($search === '' || !$type & self::SEARCH_CONTENT) {
return $result;
}
return $result;
}
/**
* @param string $search
*
* @return int
*/
private function getTypeFromSearch(string $search): int {
$char = substr($search, 0, 1);
switch ($char) {
case '@':
return self::SEARCH_ACCOUNTS;
break;
case '#':
return self::SEARCH_HASHTAGS;
break;
default:
return self::SEARCH_ALL;
}
}
}

Wyświetl plik

@ -83,6 +83,11 @@ $finger = [
'rel' => 'self',
'type' => 'application/activity+json',
'href' => $href
],
[
'rel' => 'http://ostatus.org/schema/1.0/subscribe',
'template' => urldecode(
$href = $urlGenerator->linkToRouteAbsolute('social.OStatus.subscribe', ['uri' => '{uri}']))
]
]
];

6332
package-lock.json wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -29,20 +29,21 @@
"dependencies": {
"linkifyjs": "^2.1.8",
"nextcloud-axios": "^0.1.3",
"nextcloud-vue": "^0.6.1",
"nextcloud-vue": "^0.6.3",
"tributejs": "^3.5.3",
"twemoji": "^11.3.0",
"uuid": "^3.3.2",
"v-tooltip": "^2.0.0-rc.33",
"vue": "^2.5.22",
"vue": "^2.6.6",
"vue-click-outside": "^1.0.7",
"vue-contenteditable-directive": "^1.2.0",
"vue-emoji-picker": "^1.0.1",
"vue-infinite-loading": "^2.4.3",
"vue-router": "^3.0.2",
"vue-tribute": "^1.0.1",
"vue-tribute": "^1.0.2",
"vue-twemoji": "^1.0.1",
"vuex": "^3.1.0",
"vuetrend": "^0.3.2",
"vuex-router-sync": "^5.0.0"
},
"browserslist": [
@ -53,16 +54,16 @@
"node": ">=10.0.0"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/core": "^7.3.3",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@vue/test-utils": "^1.0.0-beta.29",
"acorn": "^6.0.7",
"acorn": "^6.1.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.0.0",
"babel-loader": "^8.0.5",
"css-loader": "^2.1.0",
"eslint": "^5.13.0",
"eslint": "^5.14.1",
"eslint-config-standard": "^12.0.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^2.1.1",
@ -70,7 +71,7 @@
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^5.1.0",
"eslint-plugin-vue": "^5.2.2",
"file-loader": "^3.0.1",
"jest": "^24.0.0",
"jest-serializer-vue": "^2.0.2",
@ -81,11 +82,11 @@
"stylelint": "^8.4.0",
"stylelint-config-recommended-scss": "^3.2.0",
"stylelint-webpack-plugin": "^0.10.5",
"vue-jest": "^3.0.2",
"vue-loader": "^15.6.2",
"vue-jest": "^3.0.3",
"vue-loader": "^15.6.3",
"vue-style-loader": "^4.1.1",
"vue-template-compiler": "^2.5.22",
"webpack": "^4.29.1",
"vue-template-compiler": "^2.6.6",
"webpack": "^4.29.5",
"webpack-cli": "^3.2.3",
"webpack-merge": "^4.2.1"
},

Wyświetl plik

@ -492,19 +492,31 @@ export default {
var em = document.createTextNode(emoji.getAttribute('alt'))
emoji.replaceWith(em)
})
let content = element.innerText.trim()
let to = []
const re = /@(([\w-_.]+)(@[\w-.]+)?)/g
let hashtags = []
const mentionRegex = /@(([\w-_.]+)(@[\w-.]+)?)/g
let match = null
do {
match = re.exec(element.innerText)
match = mentionRegex.exec(content)
if (match) {
to.push(match[1])
}
} while (match)
let content = element.innerText.trim()
const hashtagRegex = /#([\w-_.]+)/g
match = null
do {
match = hashtagRegex.exec(content)
if (match) {
hashtags.push(match[1])
}
} while (match)
return {
content: content,
to: to,
hashtags: hashtags,
type: this.type
}
},

Wyświetl plik

@ -35,6 +35,9 @@
</a>
</p>
<follow-button :account="accountInfo.account" />
<button v-if="serverData.public" class="primary" @click="followRemote">
{{ t('social', 'Follow') }}
</button>
</div>
<!-- TODO: we have no details, timeline and follower list for non-local accounts for now -->
<ul v-if="accountInfo.details && accountInfo.local" class="user-profile--sections">
@ -145,7 +148,9 @@ export default {
}
},
methods: {
followRemote() {
window.open(OC.generateUrl('/apps/social/api/v1/ostatus/followRemote/' + encodeURI(this.uid)), 'followRemote', 'width=433,height=600toolbar=no,menubar=no,scrollbars=yes,resizable=yes')
}
}
}

Wyświetl plik

@ -22,23 +22,34 @@
<template>
<div class="social__wrapper">
<div v-if="allResults.length < 1" id="emptycontent" :class="{'icon-loading': loading || remoteLoading}">
<div v-if="allResults.length < 1 && hashtags.length < 1" id="emptycontent" :class="{'icon-loading': loading || remoteLoading}">
<div v-if="!loading" class="icon-search" />
<h2 v-if="!loading">
{{ t('social', 'No accounts found') }}
{{ t('social', 'No results found') }}
</h2>
<p v-if="!loading">
No accounts found for {{ term }}
{{ t('social', 'There were no results for your search:') }} {{ term }}
</p>
</div>
<div v-if="allResults.length > 0">
<div v-else>
<h3>{{ t('social', 'Searching for') }} {{ term }}</h3>
<user-entry v-for="result in allResults" :key="result.id" :item="result" />
<div v-if="hashtags.length > 0">
<li v-for="tag in hashtags" :key="tag.hashtag" class="tag">
<router-link :to="{ name: 'tags', params: {tag: tag.hashtag } }">
<span>#{{ tag.hashtag }}</span>
<trend
:data="trendData(tag.trend)"
:gradient="['#17adff', '#0082c9']" :smooth="true" :width="150"
:height="44" stroke-width="2" />
</router-link>
</li>
</div>
</div>
</div>
</template>
<style scoped>
<style scoped lang="scss">
.user-entry {
padding: 0;
}
@ -47,17 +58,35 @@
margin-top: -3px;
margin-left: 47px;
}
.tag {
list-style-type: none;
margin: 0;
padding: 0;
border-bottom: 1px solid var(--color-background-dark);
a {
display: flex;
span {
display: inline-block;
padding: 12px;
font-weight: 300;
flex-grow: 1;
}
}
}
</style>
<script>
import UserEntry from './UserEntry'
import axios from 'nextcloud-axios'
import Trend from 'vuetrend'
export default {
name: 'Search',
components: {
UserEntry
UserEntry,
Trend
},
props: {
term: {
@ -67,18 +96,19 @@ export default {
},
data() {
return {
results: [],
results: {},
loading: false,
remoteLoading: false,
match: null
match: null,
hashtags: []
}
},
computed: {
allResults() {
if (!this.match) {
return this.results
if (this.results.accounts) {
return this.results.accounts.result
}
return [this.match, ...this.results.filter((item) => item.id !== this.match.id)]
return []
}
},
watch: {
@ -90,36 +120,39 @@ export default {
this.search(this.term)
},
methods: {
trendData(trend) {
const data = [
Math.max(0, trend['10d'] - trend['3d']),
Math.max(0, trend['3d'] - trend['1d']),
Math.max(0, trend['1d'] - trend['12h']),
Math.max(0, trend['12h'] - trend['1h']),
Math.max(0, trend['1h'])
]
return data
},
search(val) {
if (this.loading) {
return
}
this.loading = true
const re = /((\w+)(@[\w.]+)+)/g
if (val.match(re) && !this.remoteLoading) {
this.remoteLoading = true
this.remoteSearch(val).then((response) => {
this.match = response.data.result.account
this.$store.commit('addAccount', { actorId: this.match.id, data: this.match })
this.remoteLoading = false
}).catch((e) => {
this.remoteLoading = false
this.match = null
})
}
this.accountSearch(val).then((response) => {
this.results = response.data.result.accounts
this.searchQuery(val).then((response) => {
this.results = response.data.result
this.loading = false
this.results.forEach((account) => {
this.results.accounts.result.forEach((account) => {
this.$store.commit('addAccount', { actorId: account.id, data: account })
})
this.hashtags = this.results.hashtags.result
})
},
accountSearch(term) {
this.loading = true
return axios.get(OC.generateUrl('apps/social/api/v1/global/accounts/search?search=' + term))
},
searchQuery(term) {
this.loading = true
return axios.get(OC.generateUrl('apps/social/api/v1/search?search=' + term))
},
remoteSearch(term) {
return axios.get(OC.generateUrl('apps/social/api/v1/global/account/info?account=' + term))
}

Wyświetl plik

@ -35,8 +35,14 @@
<script>
import { Avatar } from 'nextcloud-vue'
import * as linkify from 'linkifyjs'
import pluginTag from 'linkifyjs/plugins/hashtag'
import pluginMention from 'linkifyjs/plugins/mention'
import 'linkifyjs/string'
pluginTag(linkify)
pluginMention(linkify)
export default {
name: 'TimelineEntry',
components: {
@ -62,8 +68,11 @@ export default {
message = message.replace(/(?:\r\n|\r|\n)/g, '<br />')
message = message.linkify({
formatHref: {
email: function(href) {
return OC.generateUrl('/apps/social/@' + (href.indexOf('mailto:') === 0 ? href.substring(7) : href))
hashtag: function(href) {
return OC.generateUrl('/apps/social/timeline/tags/' + href.substring(1))
},
mention: function(href) {
return OC.generateUrl('/apps/social/@' + href.substring(1))
}
}
})

Wyświetl plik

@ -75,12 +75,19 @@ export default {
image: 'img/undraw/global.svg',
title: t('social', 'No global posts found'),
description: t('social', 'Posts from federated instances will show up here')
},
tags: {
image: 'img/undraw/profile.svg',
title: t('social', 'No posts found for this tag')
}
}
}
},
computed: {
emptyContentData() {
if (typeof this.emptyContent[this.$route.name] !== 'undefined') {
return this.emptyContent[this.$route.name]
}
if (typeof this.emptyContent[this.$route.params.type] !== 'undefined') {
return this.emptyContent[this.$route.params.type]
}

41
src/ostatus.js 100644
Wyświetl plik

@ -0,0 +1,41 @@
/*
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* 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 the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import Vue from 'vue'
import store from './store'
import OStatus from './views/OStatus'
// eslint-disable-next-line
__webpack_nonce__ = btoa(OC.requestToken)
// eslint-disable-next-line
__webpack_public_path__ = OC.linkTo('social', 'js/')
Vue.prototype.t = t
Vue.prototype.n = n
Vue.prototype.OC = OC
Vue.prototype.OCA = OCA
/* eslint-disable-next-line no-new */
new Vue({
render: h => h(OStatus),
store: store
}).$mount('#vue-content')

Wyświetl plik

@ -49,7 +49,13 @@ export default new Router({
default: Timeline
},
props: true,
name: 'timeline'
name: 'timeline',
children: [
{
path: 'tags/:tag',
name: 'tags'
}
]
},
{
path: '/:index(index.php/)?apps/social/@:account',

Wyświetl plik

@ -27,6 +27,7 @@ const state = {
timeline: {},
since: Math.floor(Date.now() / 1000) + 1,
type: 'home',
params: {},
account: ''
}
const mutations = {
@ -43,6 +44,9 @@ const mutations = {
setTimelineType(state, type) {
state.type = type
},
setTimelineParams(state, params) {
state.params = params
},
setAccount(state, account) {
state.account = account
}
@ -55,9 +59,10 @@ const getters = {
}
}
const actions = {
changeTimelineType(context, type) {
changeTimelineType(context, { type, params }) {
context.commit('resetTimeline')
context.commit('setTimelineType', type)
context.commit('setTimelineParams', params)
context.commit('setAccount', '')
},
changeTimelineTypeAccount(context, account) {
@ -88,6 +93,8 @@ const actions = {
let url
if (state.type === 'account') {
url = OC.generateUrl(`apps/social/api/v1/account/${state.account}/stream?limit=25&since=` + sinceTimestamp)
} else if (state.type === 'tags') {
url = OC.generateUrl(`apps/social/api/v1/stream/tag/${state.params.tag}?limit=25&since=` + sinceTimestamp)
} else {
url = OC.generateUrl(`apps/social/api/v1/stream/${state.type}?limit=25&since=` + sinceTimestamp)
}

Wyświetl plik

@ -0,0 +1,135 @@
<template>
<div v-if="accountInfo">
<div v-if="!serverData.local">
<h2>{{ t('social', 'Follow on Nextcloud Social') }}</h2>
<p>{{ t('social', 'Hello') }} <avatar :user="currentUser.uid" :size="16" />{{ currentUser.displayName }}</p>
<p v-if="!isFollowing">
{{ t('social', 'Please confirm that you want to follow this account:') }}
</p>
<avatar :url="avatarUrl" :disable-tooltip="true" :size="128" />
<h2>{{ displayName }}</h2>
<form v-if="!isFollowing" @submit.prevent="follow">
<input type="submit" class="primary" value="Follow">
</form>
<p v-else>
<span class="icon icon-checkmark-white" />
{{ t('social', 'You are following this account') }}
</p>
<div v-if="isFollowing">
<button @click="close">
{{ t('social', 'Close') }}
</button>
</div>
</div>
<div v-if="serverData.local">
<p>{{ t('social', 'You are going to follow:') }}</p>
<avatar :user="serverData.local" :disable-tooltip="true" :size="128" />
<h2>{{ displayName }}</h2>
<form @submit.prevent="followRemote">
<input v-model="remote" type="text" :placeholder="t('social', 'name@domain of your federation account')">
<input type="submit" class="primary" :value="t('social', 'Continue')">
</form>
<p>{{ t('social', 'This step is needed as the user is probably not registered on the same server as you are. We will redirect you to your homeserver to follow this account.') }}</p>
</div>
</div>
<div v-else :class="{ 'icon-loading-dark': !accountInfo }" />
</template>
<style scoped>
h2, p {
color: var(--color-primary-text);
}
p .icon {
display: inline-block;
}
.avatardiv {
vertical-align: -4px;
margin-right: 3px;
filter: drop-shadow(0 0 0.5rem #333);
margin-top: 10px;
margin-bottom: 20px;
}
</style>
<style>
.wrapper {
margin-top: 20px;
}
</style>
<script>
import { Avatar } from 'nextcloud-vue'
import axios from 'nextcloud-axios'
import currentuserMixin from './../mixins/currentUserMixin'
export default {
name: 'App',
components: {
Avatar
},
mixins: [currentuserMixin],
data() {
return {
remote: ''
}
},
computed: {
isFollowing() {
return this.$store.getters.isFollowingUser(this.account)
},
account() {
return this.serverData.account
},
avatarUrl() {
return OC.generateUrl('/apps/social/api/v1/global/actor/avatar?id=' + this.accountInfo.id)
},
accountInfo: function() {
return this.$store.getters.getAccount(this.serverData.account)
},
currentUser() {
return window.oc_current_user
},
displayName() {
if (typeof this.accountInfo.name !== 'undefined' && this.accountInfo.name !== '') {
return this.accountInfo.name
}
return this.account
}
},
beforeMount: function() {
// importing server data into the store
const serverDataElmt = document.getElementById('serverData')
if (serverDataElmt !== null) {
const serverData = JSON.parse(document.getElementById('serverData').dataset.server)
if (serverData.currentUser) {
window.oc_current_user = JSON.parse(JSON.stringify(serverData.currentUser))
}
this.$store.commit('setServerData', serverData)
if (this.serverData.account && !this.serverData.local) {
this.$store.dispatch('fetchAccountInfo', this.serverData.account)
}
if (this.serverData.local) {
this.$store.dispatch('fetchPublicAccountInfo', this.serverData.local)
}
}
},
methods: {
follow() {
this.$store.dispatch('followAccount', { currentAccount: this.cloudId, accountToFollow: this.account }).then(() => {
})
},
followRemote() {
axios.get(OC.generateUrl(`/apps/social/api/v1/ostatus/link/${this.serverData.local}/` + encodeURI(this.remote))).then((a) => {
window.location = a.data.result.url
})
},
close() {
window.close()
}
}
}
</script>

Wyświetl plik

@ -22,6 +22,9 @@
</div>
</transition>
<composer />
<h2 v-if="type === 'tags'">
#{{ this.$route.params.tag }}
</h2>
<timeline-list />
</div>
</template>
@ -111,7 +114,16 @@ export default {
}
},
computed: {
params() {
if (this.$route.name === 'tags') {
return { tag: this.$route.params.tag }
}
return {}
},
type() {
if (this.$route.name === 'tags') {
return 'tags'
}
if (this.$route.params.type) {
return this.$route.params.type
}
@ -128,7 +140,7 @@ export default {
}
},
beforeMount: function() {
this.$store.dispatch('changeTimelineType', this.type)
this.$store.dispatch('changeTimelineType', { type: this.type, params: this.params })
if (this.showInfo) {
this.$store.dispatch('fetchAccountInfo', this.nextcloudAccount)
}

Wyświetl plik

@ -0,0 +1,28 @@
<?php
/**
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* 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 the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
script('social', 'ostatus');
style('social', 'style');
?>
<span id="serverData" data-server="<?php p(json_encode($_['serverData']));?>"></span>
<div id="vue-content"></div>

Wyświetl plik

@ -3,11 +3,15 @@ const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: path.join(__dirname, 'src', 'main.js'),
entry: {
social: path.join(__dirname, 'src', 'main.js'),
ostatus: path.join(__dirname, 'src', 'ostatus.js'),
},
output: {
path: path.resolve(__dirname, './js'),
publicPath: '/js/',
filename: 'social.js'
filename: '[name].js',
chunkFilename: '[name].[chunkhash].js'
},
module: {
rules: [