Skip to content
This repository was archived by the owner on May 25, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions generators/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ module.exports = class extends Generator {
src: 'src/containers/Profile/**',
dest: 'src/containers/Profile',
},
{
src: 'src/containers/TextEditor/**',
dest: 'src/containers/TextEditor',
},
{
src: 'src/containers/TicTacToe/**',
dest: 'src/containers/TicTacToe',
Expand Down
34 changes: 31 additions & 3 deletions generators/app/templates/public/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@
"notifications": {
"success": "Success",
"error": "Error",
"unknownError": "An unknown error has occurred"
"unknownError": "An unknown error has occurred",
"404": "Editing a new document.",
"errorLoading": "There was an error loading your file, please try again.",
"errorSaving": "There was an error saving your file, please try again.",
"errorFetching": "Error fetching document.",
"accessGranted": "Co-editor now has read/write access.",
"errorGrantingAccess": "There was an error giving access to your file, please try again.",
"saved": "Your file was saved successfully.",
"notEditable": "(not editable)",
"notSharable": "(log in and load a doc on your own pod to give others access)"
},
"login": {
"title": "Hi! Welcome to Solid.",
Expand All @@ -24,7 +33,7 @@
"btnTxtProvider": "Log In with Provider",
"errors": {
"unknown": "Something is wrong, please try again...",
"webIdNotValid": "WeibID is not valid",
"webIdNotValid": "WebID is not valid",
"emptyProvider": "Solid Provider is required",
"emptyWebId": "Valid WebID is required"
}
Expand All @@ -40,6 +49,7 @@
"welcome": "Welcome",
"logOut": "Log out",
"profile": "Profile",
"text-editor": "Text Editor",
"changeLanguage": "Change Language",
"languages": {
"en": "English (English)",
Expand Down Expand Up @@ -186,6 +196,12 @@
"href": "https://solidsdk.inrupt.net/public/general/en/app-inbox-cannot-be-created.html"
}
},
"errorFormRender": {
"link": {
"label": "Learn more",
"href": "https://solidsdk.inrupt.net/public/general/en/form-rendering-errors.html"
}
},
"formLanguage": {
"shacl": "SHACL",
"shaclExtension": "SHACL + RDF Extension",
Expand All @@ -212,7 +228,19 @@
"viewBtn": "View",
"editBtn": "Edit",
"viewMode": "View Mode",
"editMode": "Edit Mode"
"editMode": "Edit Mode",
"formSaved": "Form successfully saved",
"formNotLoaded": "Form could not be rendered",
"fieldDeleted": "Form field successfully deleted",
"fieldAdded": "New form field successfully added"
}
},
"editor": {
"explanation": "This simple text editor enables you to create a plaintext file and save it in a Pod. To make this very clear for the demo you provide a URL as an absolute path to the file in the Pod. The Pod can be the one this App has been logged into or another person's Pod. To write, and read, into another person's Pod the owner will have had to provide you with write permission.",
"url": "URL",
"friend": "Co-editor's webid",
"load": "Load",
"save": "Save",
"grantAccess": "Grant Access"
}
}
21 changes: 20 additions & 1 deletion generators/app/templates/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@
"notifications": {
"success": "Success",
"error": "Error",
"unknownError": "An unknown error has occurred"
"unknownError": "An unknown error has occurred",
"404": "Editing a new document.",
"errorLoading": "There was an error loading your file, please try again.",
"errorSaving": "There was an error saving your file, please try again.",
"errorFetching": "Error fetching document.",
"accessGranted": "Co-editor now has read/write access.",
"errorGrantingAccess": "There was an error giving access to your file, please try again.",
"saved": "Your file was saved successfully.",
"notEditable": "(not editable)",
"notSharable": "(log in and load a doc on your own pod to give others access)"
},
"login": {
"title": "Hi! Welcome to Solid.",
Expand Down Expand Up @@ -40,6 +49,7 @@
"welcome": "Welcome",
"logOut": "Log out",
"profile": "Profile",
"text-editor": "Text Editor",
"changeLanguage": "Change Language",
"languages": {
"en": "English (English)",
Expand Down Expand Up @@ -128,6 +138,7 @@
"idLabel": "Game ID",
"opponentWebIDLabel": "Opponent WebID",
"nogames": "No game were found",
"invitationAccept": "has invited you to play a game of TicTacToe. Would you like to play?",
"invitationTemplate": "<0><0><0>{{- name}}</0> has invited you to play a game of TicTacToe.</0><1>Would you like to play?</1></0>",
"invitationAcceptText": "Accept",
"invitationDeclineText": "Decline",
Expand Down Expand Up @@ -223,5 +234,13 @@
"fieldDeleted": "Form field successfully deleted",
"fieldAdded": "New form field successfully added"
}
},
"editor": {
"explanation": "This simple text editor enables you to create a plaintext file and save it in a Pod. To make this very clear for the demo you provide a URL as an absolute path to the file in the Pod. The Pod can be the one this App has been logged into or another person's Pod. To write, and read, into another person's Pod the owner will have had to provide you with write permission.",
"url": "URL",
"friend": "Co-editor's webid",
"load": "Load",
"save": "Save",
"grantAccess": "Grant Access"
}
}
21 changes: 20 additions & 1 deletion generators/app/templates/public/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@
"notifications": {
"success": "Éxito",
"error": "Error",
"unknownError": "Ha occurido un error desconocido"
"unknownError": "Ha occurido un error desconocido",
"404": "Editando un documento nuevo.",
"errorLoading": "Había un error al cargar tu documento. Por favor, intenta de nuevo.",
"errorSaving": "Había un error al guardar tu documento. Por favor, intenta de nuevo.",
"errorFetching": "Error recogiendo fichero.",
"accessGranted": "Collaborador ahora tiene acceso de leer y escribir.",
"errorGrantingAccess": "Había un error al dar acceso a tu documento. Por favor, intenta de nuevo.",
"saved": "Tu documento ha sido guardado al pod.",
"notEditable": "(no editable)",
"notSharable": "(logéate y carga undocumento en tu própio pod para poder dar acceso)"
},
"login": {
"title": "Hola! Bienvenido a Solid.",
Expand Down Expand Up @@ -40,6 +49,7 @@
"welcome": "Bienvenida",
"logOut": "Cerrar Sesión",
"profile": "Perfil",
"text-editor": "Editor de Textos",
"changeLanguage": "Cambiar Lenguaje",
"languages": {
"en": "English (Inglés)",
Expand Down Expand Up @@ -128,6 +138,7 @@
"idLabel": "ID del Juego",
"opponentWebIDLabel": "WebID del Oponente",
"nogames": "No se han encontrado juegos",
"invitationAccept": "te ha invitado a jugar una partida de Tres en Linea. Te gustaría jugar?",
"invitationTemplate": "<0><0><0>{{- name}}</0> te ha invitado a un juego de Tres en Linea.</0><1>Quieres aceptar el juego?</1></0>",
"invitationAcceptText": "Aceptar",
"invitationDeclineText": "Rechazar",
Expand Down Expand Up @@ -223,5 +234,13 @@
"fieldDeleted": "Campo de formulario eliminado correctamente",
"fieldAdded": "Campo de formulario agregado con éxito"
}
},
"editor": {
"explanation": "Aqui tiene un simple editor de textos con que puede crear un fichero de texto plano y guardarlo en un Pod. Para mostrarlo claramente en este demo, hay que proveer una URL que directamente identifica el fichero en el Pod. El Pod puede ser el del mismo usuario conectado, o de otra persona. Para poder escribir, y leer, al Pod de otra persona, el proprietario tendrá que dar permiso de escribir, usando el botón de 'Dar Acceso' abajo.",
"url": "URL",
"friend": "Webid de collaborador",
"load": "Cargar",
"save": "Guardar",
"grantAccess": "Dar Acceso"
}
}
3 changes: 3 additions & 0 deletions generators/app/templates/src/containers/TextEditor/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import TextEditor from './text-editor.component';

export default TextEditor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/* eslint-disable constructor-super */
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { TextEditorWrapper, TextEditorContainer, Header, Form, FullGridSize, Button, Label, Input, TextArea, WebId } from './text-editor.style';
import SolidAuth from 'solid-auth-client';
import { successToaster, errorToaster } from '@utils';
import { fetchDocument } from 'tripledoc';
import { AccessControlList } from '@inrupt/solid-react-components';
import { useWebId } from '@solid/react';

const pim = { storage: 'http://www.w3.org/ns/pim/space#storage' };

function extractWacAllow(response) {
// WAC-Allow: user="read write append control",public="read"
let modes = {
user: {
read: false,
append: false,
write: false,
control: false
},
public: {
read: false,
append: false,
write: false,
control: false
}
};
const wacAllowHeader = response.headers.get('WAC-Allow');
if (wacAllowHeader) {
wacAllowHeader // 'user="read write append control",public="read"'
.split(',') // ['user="read write append control"', 'public="read"']
.map(str => str.trim())
.forEach(statement => { // 'user="read write append control"'
const parts = statement.split('='); // ['user', '"read write control"']
if (parts.length >= 2 && ['user', 'public'].indexOf(parts[0]) !== -1 && parts[1].length > 2) {
const modeStr = parts[1].replace(/"/g, ''); // 'read write control' or ''
if (modeStr.length) {
modeStr.split(' ').forEach(mode => {
modes[parts[0]][mode] = true;
});
}
}
});
}
return modes;
}

export const Editor = (props) => {
const { t } = useTranslation();
const [url, setUrl] = useState('');
const [friend, setFriend] = useState('https://example-friend.com/profile/card#me');
const [text, setText] = useState('');
const [profileDoc, setProfileDoc] = useState();
const webId = useWebId();
useEffect(() => {
async function fetchProfileDoc () {
if (webId) {
setProfileDoc(await fetchDocument(webId));
}
}
fetchProfileDoc();
}, [webId]);
useEffect(() => {
if (profileDoc && !url) {
const sub = profileDoc.getSubject(webId);
const storageRoot = sub.getNodeRef(pim.storage);
if (storageRoot) {
const exampleUrl = new URL('/share/some-doc.txt', storageRoot);
setUrl(exampleUrl.toString());
}
}
}, [profileDoc]);

const [loaded, setLoaded] = useState(false);
const [editable, setEditable] = useState(false);
const [sharable, setSharable] = useState(false);

function handleUrlChange(event) {
event.preventDefault();
setUrl(event.target.value);
}

function handleFriendChange(event) {
event.preventDefault();
setFriend(event.target.value);
}

function handleTextChange(event) {
event.preventDefault();
setText(event.target.value);
}

function handleLoad(event) {
event.preventDefault();
const doc = SolidAuth.fetch(url);
doc.then(async (response) => {
const text = await response.text();
if (response.ok) {
setText(text);
} else if (response.status === 404) {
successToaster(t('notifications.404'));
} else {
errorToaster(t('notifications.errorLoading'));
}
const wacAllowModes = extractWacAllow(response);
setEditable(wacAllowModes.user.write);
setSharable(wacAllowModes.user.control);
setLoaded(true);
}).catch((e) => {
errorToaster(t('notifications.errorFetching'));
});
} // assuming the logged in user doesn't change without a page refresh

async function handleShare(event) {
event.preventDefault();
try {
const permissions = [
{
agents: [friend],
modes: [AccessControlList.MODES.READ, AccessControlList.MODES.WRITE]
}
];
const ACLFile = new AccessControlList(webId, url);
await ACLFile.createACL(permissions);
successToaster(t('notifications.accessGranted'));
} catch (e) {
errorToaster(t('notifications.errorGrantingAccess'));
}
}

async function handleSave(event) {
event.preventDefault();
// Not using TripleDoc or LDFlex here, because this is not an RDF document.
const result = await SolidAuth.fetch(url, {
method: 'PUT',
body: text,
headers: {
'Content-Type': 'text/plain'
}
});

if (result.ok) {
successToaster(t('notifications.saved'));
} else if(result.ok === false) {
errorToaster(t('notifications.errorSaving'));
}
}

return (
<Form>
<FullGridSize>
<WebId><b>Connected as: <a href={webId}>{webId}</a></b></WebId>
</FullGridSize>
<FullGridSize>
<Label>
{t('editor.url')}:
<Input type="text" size="200" value={url} onChange={handleUrlChange} />
</Label>
<div class="input-wrap">
<Button className="ids-link-filled ids-link-filled--primary button" onClick={handleLoad}>{t('editor.load')}</Button>
{editable ?
<Button className="ids-link-filled ids-link-filled--secondary button" onClick={handleSave}>{t('editor.save')}</Button>
: (loaded ? t('notifications.notEditable') : '')}
</div>
</FullGridSize>
<FullGridSize>
<TextArea value={text} onChange={handleTextChange} cols={40} rows={10} />
</FullGridSize>
{sharable ? <FullGridSize>
<Label>
{t('editor.friend')}:
<Input type="text" size="200" value={friend} onChange={handleFriendChange} />
</Label>
<Button className="ids-link-stroke ids-link-stroke--primary button" onClick={handleShare}>{t('editor.grantAccess')}</Button>
</FullGridSize>: t('notifications.notSharable')}
</Form>
);
}

/**
* A React component page that is displayed when there's no valid route. Users can click the button
* to get back to the home/welcome page.
*/
const TextEditor = () => {
const { t } = useTranslation();
return (
<TextEditorWrapper>
<TextEditorContainer>
<Header>
<p>
{t('editor.explanation')}
</p>
</Header>
<Editor/>
</TextEditorContainer>
</TextEditorWrapper>
);
};

export default TextEditor;
Loading