Commit 1685a178 authored by Laurent Wouters's avatar Laurent Wouters
Browse files

[feature] Browser support for supplementary resources

parent 5da732a18098
...@@ -3,14 +3,6 @@ ...@@ -3,14 +3,6 @@
SCRIPT="$(readlink -f "$0")" SCRIPT="$(readlink -f "$0")"
ROOT="$(dirname "$SCRIPT")" ROOT="$(dirname "$SCRIPT")"
echo ""
echo "=========================================="
echo "= Building @logilab/librdf"
echo "=========================================="
(cd "$ROOT/librdf"; rm -rf build)
(cd "$ROOT/librdf"; npm install)
(cd "$ROOT/librdf"; npm run build)
echo "" echo ""
echo "==========================================" echo "=========================================="
echo "= Building @logilab/libview" echo "= Building @logilab/libview"
...@@ -24,7 +16,6 @@ echo "==========================================" ...@@ -24,7 +16,6 @@ echo "=========================================="
echo "= Building @logilab/views-logilab" echo "= Building @logilab/views-logilab"
echo "==========================================" echo "=========================================="
(cd "$ROOT/views-logilab"; rm -rf build) (cd "$ROOT/views-logilab"; rm -rf build)
(cd "$ROOT/views-logilab"; rm -rf node_modules/@logilab/librdf)
(cd "$ROOT/views-logilab"; rm -rf node_modules/@logilab/libview) (cd "$ROOT/views-logilab"; rm -rf node_modules/@logilab/libview)
(cd "$ROOT/views-logilab"; npm install) (cd "$ROOT/views-logilab"; npm install)
(cd "$ROOT/views-logilab"; npm run build) (cd "$ROOT/views-logilab"; npm run build)
...@@ -34,7 +25,6 @@ echo "==========================================" ...@@ -34,7 +25,6 @@ echo "=========================================="
echo "= Building @logilab/ld-browser" echo "= Building @logilab/ld-browser"
echo "==========================================" echo "=========================================="
(cd "$ROOT/extension"; rm -rf build) (cd "$ROOT/extension"; rm -rf build)
(cd "$ROOT/extension"; rm -rf node_modules/@logilab/librdf)
(cd "$ROOT/extension"; rm -rf node_modules/@logilab/libview) (cd "$ROOT/extension"; rm -rf node_modules/@logilab/libview)
(cd "$ROOT/extension"; npm install) (cd "$ROOT/extension"; npm install)
(cd "$ROOT/extension"; npm run build) (cd "$ROOT/extension"; npm run build)
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
"author": "LOGILAB <contact@logilab.fr>", "author": "LOGILAB <contact@logilab.fr>",
"license": "LGPL-3.0", "license": "LGPL-3.0",
"dependencies": { "dependencies": {
"@logilab/librdf": "file:../librdf",
"@logilab/libview": "file:../libview", "@logilab/libview": "file:../libview",
"bootstrap": "^4.1.1", "bootstrap": "^4.1.1",
"jquery": "^1.12.4", "jquery": "^1.12.4",
......
...@@ -328,9 +328,20 @@ class LDBrowserImpl implements LDBrowser { ...@@ -328,9 +328,20 @@ class LDBrowserImpl implements LDBrowser {
uris.forEach(async (uri: string) => { uris.forEach(async (uri: string) => {
let childResource = await self.resolveResourceData(uri); let childResource = await self.resolveResourceData(uri);
let childStore = await self.resolveRdfStore(childResource); let childStore = await self.resolveRdfStore(childResource);
childStore.statements.forEach((statement: $rdf.Statement) => childStore.statements.forEach((statement: $rdf.Statement) => {
parentStore.addStatement(statement) parentStore.add(
); statement.subject.termType == "NamedNode"
? parentStore.sym(statement.subject.value)
: statement.subject,
statement.predicate.termType == "NamedNode"
? parentStore.sym(statement.predicate.value)
: statement.predicate,
statement.object.termType == "NamedNode"
? parentStore.sym(statement.object.value)
: statement.object,
statement.graph
);
});
resource.supplements.push(uri); resource.supplements.push(uri);
onFinishedPart(); onFinishedPart();
}); });
......
{
"name": "@logilab/librdf",
"version": "0.1.0",
"description": "Library for complementary RDF utilities.",
"scripts": {
"build": "webpack",
"clean": "rm -rf build/*",
"test": "echo \"Error: no test specified\" && exit 1",
"build:production": "NODE_ENV=production webpack"
},
"main": "./build/index.js",
"types": "./build/lib/index.d.ts",
"repository": {
"type": "hg",
"url": "https://bitbucket.org/laurentw/logilab-cubicweb-client"
},
"keywords": [
"DataBrowser",
"Typescript"
],
"author": "LOGILAB <contact@logilab.fr>",
"license": "LGPL-3.0",
"dependencies": {
"rdflib": "^0.17.0"
},
"devDependencies": {
"@types/node": "^10.5.3",
"@types/rdflib": "^0.17.0",
"source-map-loader": "^0.2.3",
"ts-loader": "^4.4.2",
"typescript": "^2.9.2",
"webpack": "^4.0.1",
"webpack-cli": "^2.0.10"
}
}
{
"compilerOptions": {
"outDir": "./lib/",
"sourceMap": true,
"declaration": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "es6",
"lib": [ "es6", "dom" ],
"jsx": "react"
},
"include": [
"./src/**/*"
],
}
\ No newline at end of file
const path = require("path");
const isProd = process.env.NODE_ENV === "production";
module.exports = [
{
entry: "./src/index.ts",
mode: isProd ? "production" : "development",
output: {
path: path.resolve(__dirname, "build/"),
filename: "index.js",
libraryTarget: "umd",
library: "librdf",
umdNamedDefine: true
},
devtool: "source-map",
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"]
},
module: {
rules: [
{ test: /\.tsx?$/, loader: "ts-loader" },
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
]
}
}
];
...@@ -21,5 +21,7 @@ ...@@ -21,5 +21,7 @@
import * as application from "./application"; import * as application from "./application";
import * as definition from "./view-def"; import * as definition from "./view-def";
import * as implementation from "./view-impl"; import * as implementation from "./view-impl";
import * as rdfEntities from "./rdf-entities";
import * as rdfMeta from "./rdf-meta";
export { application, definition, implementation }; export { application, definition, implementation, rdfEntities, rdfMeta };
...@@ -19,45 +19,6 @@ ...@@ -19,45 +19,6 @@
******************************************************************************/ ******************************************************************************/
import * as $rdf from "rdflib"; import * as $rdf from "rdflib";
require("rdflib");
/**
* Gets the value for the propert of a resource
* @param store The RDF store containing the data
* @param resource The resource
* @param property The property
*/
export function getValueOf(
store: $rdf.Formula,
resource: string,
property: string
): $rdf.Node {
return store.any(
$rdf.sym(resource),
$rdf.sym(property),
undefined,
undefined
);
}
/**
* Gets the values for the propert of a resource
* @param store The RDF store containing the data
* @param resource The resource
* @param property The property
*/
export function getValuesOf(
store: $rdf.Formula,
resource: string,
property: string
): $rdf.Node[] {
return store.each(
$rdf.sym(resource),
$rdf.sym(property),
undefined,
undefined
);
}
/** /**
* A single RDF entity that may have aliases * A single RDF entity that may have aliases
......
/*******************************************************************************
* Copyright 2003-2018 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
* contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
*
* This file is part of CubicWeb.
*
* CubicWeb is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 2.1 of the License, or (at your option)
* any later version.
*
* CubicWeb 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import * as $rdf from "rdflib";
import { RdfEntity, RdfEntityStore } from "./rdf-entities";
import { Language, ViewRenderer, RenderingContext } from "./application";
/**
* Represents some ontological knowledge about a domain
*/
export interface Ontology {
/**
* The known aliases
*/
aliases: string[][];
/**
* The known inverse relations
*/
inverses: { [uri: string]: string };
}
/**
* A resolver of objects from entities
*/
export type ObjectResolver = (entity: RdfEntity, metaClass: MetaClass) => any;
/**
* Represents the data about a property
*/
export interface MetaProperty {
/**
* The associated property's URI
*/
readonly uri: string;
/**
* The property's name
*/
readonly name: string;
/**
* Whether the property is a vector property
*/
readonly isVector: boolean;
/**
* Whether the property is an object property
*/
readonly isObject: boolean;
/**
* Loads the value(s) of this property
* @param entity An entity
* @param languages The selected languages
* @param resolver The object resolver to use
*/
load(entity: RdfEntity, languages: Language[], resolver: ObjectResolver): any;
}
/**
* Represents the meta data about a data property
*/
export class MetaPropertyData implements MetaProperty {
/**
* The associated property's URI
*/
public readonly uri: string;
/**
* The property's name
*/
public readonly name: string;
/**
* Whether the property is a vector property
*/
public readonly isVector: boolean;
/**
* Whether the property is an object property
*/
public readonly isObject: boolean;
/**
* Initialies this property
* @param name The property's name
* @param uri The associated property's URI
* @param isVector Whether the property is a vector property
*/
constructor(name: string, uri: string, isVector: boolean = false) {
this.name = name;
this.uri = uri;
this.isVector = isVector;
this.isObject = false;
}
/**
* Loads the value(s) of this property
* @param entity An entity
* @param languages The selected languages
* @param resolver The object resolver to use
*/
public load(
entity: RdfEntity,
languages: Language[],
resolver: ObjectResolver
): any {
let literals = entity.getValuesForS(this.uri);
literals = literals.filter((node: $rdf.Node) => node.termType == "Literal");
if (literals.length == 0) return this.isVector ? [] : null;
let matching = literals
.filter((literal: $rdf.Literal) => {
for (var l = 0; l != languages.length; l++) {
if (
literal.lang == languages[l].iso639_1 ||
literal.lang == languages[l].iso639_2
)
return true;
}
return false;
})
.map((literal: $rdf.Literal) => literal.value);
if (matching.length != 0) return this.isVector ? matching : matching[0];
return this.isVector
? literals.map((literal: $rdf.Literal) => literal.value)
: literals[0].value;
}
}
/**
* Represents the meta data about an object property
*/
export class MetaPropertyObject implements MetaProperty {
/**
* The associated property's URI
*/
public readonly uri: string;
/**
* The property's name
*/
public readonly name: string;
/**
* Whether the property is a vector property
*/
public readonly isVector: boolean;
/**
* Whether the property is an object property
*/
public readonly isObject: boolean;
/**
* The meta-class for the type of the property, if this is an object property
*/
public readonly metaClass: MetaClass;
/**
* Initialies this property
* @param name The property's name
* @param uri The associated property's URI
* @param metaClass The meta-class for the type of the property, if this is an object property
* @param isVector Whether the property is a vector property
*/
constructor(
name: string,
uri: string,
metaClass: MetaClass,
isVector: boolean = false
) {
this.name = name;
this.uri = uri;
this.metaClass = metaClass;
this.isVector = isVector;
this.isObject = true;
}
/**
* Loads the value(s) of this property
* @param entity An entity
* @param languages The selected languages
* @param resolver The object resolver to use
*/
public load(
entity: RdfEntity,
languages: Language[],
resolver: ObjectResolver
): any {
let result = entity.getEntitiesForS(this.uri).map((e: RdfEntity) => {
let focus = e.getEntityForS("http://xmlns.com/foaf/0.1/focus");
let real = focus != null ? focus : e;
return resolver(real, this.metaClass);
});
return this.isVector ? result : result.length == 0 ? null : result[0];
}
}
/**
* A metaclass that can be loaded from a dataset
*/
export class MetaClass {
/**
* The class's meta properties
*/
public properties: MetaProperty[] = [];
/**
* Loads an instance of this metaclass from the specified entity
* @param entity An entity
* @param languages The selected languages
* @param result The resulting object
* @param resolver The object resolver to use
*/
public loadProperties(
entity: RdfEntity,
languages: Language[],
result: any,
resolver: ObjectResolver
): void {
this.properties.forEach((property: MetaProperty) => {
result[property.name] = property.load(entity, languages, resolver);
});
}
}
/**
* A loading item
*/
class Item {
/**
* The entity to load
*/
entity: RdfEntity;
/**
* The associated metaclass
*/
metaClass: MetaClass;
/**
* The resulting object
*/
object: any;
}
/**
* The loader of entities
*/
class Loader {
constructor() {
this.items = [];
this.resolve = this.resolve.bind(this);
this.close = this.close.bind(this);
}
/**
* The items
*/
items: Item[];
/**
* Resolves an object from an entity and its meta-class
* @param entity An entity
* @param metaClass The associated meta-class
*/
resolve(entity: RdfEntity, metaClass: MetaClass): any {
let found = this.items.find((item: Item) => item.entity === entity);
if (found == undefined) {
found = {
entity: entity,
metaClass: metaClass,
object: { uri: entity.uris[0] }
};
this.items.push(found);
}
return found.object;
}
/**
* Closes this loader
* @param languages The selected languages
*/
close(languages: Language[]): void {
let i = 0;
while (i < this.items.length) {
this.items[i].metaClass.loadProperties(
this.items[i].entity,
languages,
this.items[i].object,
this.resolve
);
i++;
}
}
}
/**
* Loads the data for the entity of interest
* @param renderer The current renderer
* @param context The current rendering context
* @param target The current RDF entity to be rendered
*/
export function loadEntity<T>(
renderer: ViewRenderer,
context: RenderingContext,
ontology: Ontology,
metaClass: MetaClass,
target: string
): T {
let repo = new RdfEntityStore(
context.store,
ontology.aliases,
ontology.inverses
);
let entity = repo.entityForUri(target);
let focus = entity.getEntityForS("http://xmlns.com/foaf/0.1/focus");
if (focus != null) entity = focus;
let loader = new Loader();
loader.resolve(entity, metaClass);
loader.close(renderer.getLanguagesFor(context, target));
return loader.items[0].object;
}
...@@ -104,29 +104,33 @@ function fetchImplementation( ...@@ -104,29 +104,33 @@ function fetchImplementation(
resolver(descriptor.resourceMain) resolver(descriptor.resourceMain)
.then((content: string) => { .then((content: string) => {
let EMBEDDED = IMPL_EMBEDDED; let EMBEDDED = IMPL_EMBEDDED;
let result = eval(content); try {
if (result == null || result == undefined) { let result = eval(content);
reject( if (result == null || result == undefined) {
"Failed to load definition of " + reject(
descriptor.name + "Failed to load definition of " +
": Cannot find entrypoint" descriptor.name +
); ": Cannot find entrypoint"
return; );
} return;
if ( }
descriptor.entrypoint != null && if (
descriptor.entrypoint.length > 0 descriptor.entrypoint != null &&
) { descriptor.entrypoint.length > 0
result = result[descriptor.entrypoint]; ) {
} result = result[descriptor.entrypoint];
if (result == null || result == undefined) { }
reject( if (result == null || result == undefined) {
"Failed to load definition of " + reject(
descriptor.name + "Failed to load definition of " +
": Cannot find entrypoint" descriptor.name +
); ": Cannot find entrypoint"
} else { );
resolve(result); } else {
resolve(result);
}
} catch (e) {
reject(e);
} }
}) })
.catch((reason: string) => { .catch((reason: string) => {
......