diff --git a/asl_articles/__init__.py b/asl_articles/__init__.py index 75bd7b0..1c26285 100644 --- a/asl_articles/__init__.py +++ b/asl_articles/__init__.py @@ -108,6 +108,7 @@ import asl_articles.authors #pylint: disable=cyclic-import import asl_articles.scenarios #pylint: disable=cyclic-import import asl_articles.images #pylint: disable=cyclic-import import asl_articles.tags #pylint: disable=cyclic-import +import asl_articles.docs #pylint: disable=cyclic-import import asl_articles.utils #pylint: disable=cyclic-import # initialize diff --git a/asl_articles/config/site.cfg.example b/asl_articles/config/site.cfg.example index b1668bd..d69a1df 100644 --- a/asl_articles/config/site.cfg.example +++ b/asl_articles/config/site.cfg.example @@ -4,3 +4,6 @@ ; sqlite:////home/pacman-ghost/asl-articles.db ; postgresql://USER:PASS@localhost/asl_articles DB_CONN_STRING = ... + +; Base directory for external documents. +EXTERNAL_DOCS_BASEDIR = ... diff --git a/asl_articles/docs.py b/asl_articles/docs.py new file mode 100644 index 0000000..d054709 --- /dev/null +++ b/asl_articles/docs.py @@ -0,0 +1,15 @@ +""" Provide access to external documents. """ + +from flask import abort, send_from_directory + +from asl_articles import app + +# --------------------------------------------------------------------- + +@app.route( "/docs/" ) +def get_external_doc( path ): + """Return an external document.""" + base_dir = app.config.get( "EXTERNAL_DOCS_BASEDIR" ) + if not base_dir: + abort( 404 ) + return send_from_directory( base_dir, path ) diff --git a/docker-compose.yml b/docker-compose.yml index dd580b8..1c196f3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,9 @@ # This is done via the SQLITE variable, but since Docker doesn't allow any way to do things conditionally, # it needs to be set even if it's not being used :-/ # +# Similarly, EXTERNAL_DOCS_BASEDIR is the base directory for external documents that we want to link to, +# but it needs to be set even if it's not being used :-/ +# # See the run-containers.sh script that manages all of this. version: "3" @@ -29,5 +32,6 @@ services: - "5002:5000" volumes: - $SQLITE:/data/sqlite.db + - $EXTERNAL_DOCS_BASEDIR:/data/docs/ environment: - DBCONN diff --git a/docker/config/site.cfg b/docker/config/site.cfg index c72edbe..958c7ec 100644 --- a/docker/config/site.cfg +++ b/docker/config/site.cfg @@ -2,3 +2,5 @@ FLASK_HOST = 0.0.0.0 IS_CONTAINER = 1 + +EXTERNAL_DOCS_BASEDIR = /data/docs/ diff --git a/run-containers.sh b/run-containers.sh index 261dbaf..db3928b 100755 --- a/run-containers.sh +++ b/run-containers.sh @@ -3,15 +3,17 @@ # parse the command-line arguments if [ -z "$1" ]; then - echo "Usage: `basename "$0"` " + echo "Usage: `basename "$0"` " echo " Build and launch the \"asl-articles\" containers, using the specified database e.g." echo " ~/asl-articles.db (path to a SQLite database)" echo " postgresql://USER:PASS@host/dbname (database connection string)" echo " Note that the database server address is relative to the container i.e. NOT \"localhost\"." echo + echo " If you want link articles to their original documents, specify a base directory for the documents." + echo echo " The TAG env variable should also be set to specify which containers to run e.g." echo " TAG=testing ./run.sh /tmp/asl-articles.db" - exit 1 + exit 0 fi if [ -f "$1" ]; then # connect to a SQLite database @@ -22,6 +24,17 @@ else export SQLITE=/dev/null export DBCONN=$1 fi +if [ ! -z "$2" ]; then + # set the base directory for external documents + export EXTERNAL_DOCS_BASEDIR=$2 + if [ ! -d "$EXTERNAL_DOCS_BASEDIR" ]; then + echo "Invalid document base directory: $EXTERNAL_DOCS_BASEDIR" + exit 1 + fi +else + # FUDGE! This needs to be set, even if it's not being used :-/ + export EXTERNAL_DOCS_BASEDIR=/dev/null +fi # initialize if [ "$TAG" == "testing" ]; then diff --git a/web/src/App.js b/web/src/App.js index 14671d8..86ec333 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -425,6 +425,12 @@ export default class App extends React.Component url += "?foo=" + Math.random() ; // FUDGE! To bypass the cache :-/ return url ; } + makeExternalDocUrl( url ) { + // generate a URL for an external document + if ( url.substr( 0, 2 ) === "$/" ) + url = url.substr( 2 ) ; + return this.makeFlaskUrl( "/docs/" + encodeURIComponent(url) ) ; + } _onStartupTask( taskId ) { // flag that the specified startup task has completed diff --git a/web/src/ArticleSearchResult.js b/web/src/ArticleSearchResult.js index 59caad8..e017e94 100644 --- a/web/src/ArticleSearchResult.js +++ b/web/src/ArticleSearchResult.js @@ -4,7 +4,7 @@ import { ArticleSearchResult2 } from "./ArticleSearchResult2.js" ; import "./ArticleSearchResult.css" ; import { PublicationSearchResult } from "./PublicationSearchResult.js" ; import { gAppRef } from "./index.js" ; -import { makeScenarioDisplayName, applyUpdatedVals, removeSpecialFields, makeCommaList } from "./utils.js" ; +import { makeScenarioDisplayName, applyUpdatedVals, removeSpecialFields, makeCommaList, isLink } from "./utils.js" ; const axios = require( "axios" ) ; @@ -22,6 +22,17 @@ export class ArticleSearchResult extends React.Component const pub = gAppRef.caches.publications[ this.props.data.pub_id ] ; const image_url = gAppRef.makeFlaskImageUrl( "article", this.props.data.article_image_id, true ) ; + // prepare the article's URL + let article_url = this.props.data.article_url ; + if ( article_url ) { + if ( ! isLink( article_url ) ) + article_url = gAppRef.makeExternalDocUrl( article_url ) ; + } else if ( pub && pub.pub_url ) { + article_url = gAppRef.makeExternalDocUrl( pub.pub_url ) ; + if ( article_url.substr( article_url.length-4 ) === ".pdf" && this.props.data.article_pageno ) + article_url += "#page=" + this.props.data.article_pageno ; + } + // prepare the authors let authors = [] ; if ( this.props.data[ "authors!" ] ) { @@ -119,8 +130,8 @@ export class ArticleSearchResult extends React.Component } - { this.props.data.article_url && - + { article_url && + Open article. } diff --git a/web/src/PublicationSearchResult.js b/web/src/PublicationSearchResult.js index 003f253..0f0d607 100644 --- a/web/src/PublicationSearchResult.js +++ b/web/src/PublicationSearchResult.js @@ -4,7 +4,7 @@ import "./PublicationSearchResult.css" ; import { PublicationSearchResult2 } from "./PublicationSearchResult2.js" ; import { PUBLICATION_EXCESS_ARTICLE_THRESHOLD } from "./constants.js" ; import { gAppRef } from "./index.js" ; -import { makeCollapsibleList, pluralString, applyUpdatedVals, removeSpecialFields } from "./utils.js" ; +import { makeCollapsibleList, pluralString, applyUpdatedVals, removeSpecialFields, isLink } from "./utils.js" ; const axios = require( "axios" ) ; @@ -20,6 +20,11 @@ export class PublicationSearchResult extends React.Component const publ = gAppRef.caches.publishers[ this.props.data.publ_id ] ; const image_url = PublicationSearchResult.makeImageUrl( this.props.data ) ; + // prepare the publication's URL + let pub_url = this.props.data.pub_url ; + if ( pub_url && ! isLink(pub_url) ) + pub_url = gAppRef.makeExternalDocUrl( pub_url ) ; + // prepare the tags let tags = [] ; if ( this.props.data[ "tags!" ] ) { @@ -89,8 +94,8 @@ export class PublicationSearchResult extends React.Component onClick = { () => gAppRef.searchForPublication( this.props.data.pub_id ) } title = "Show this publication." /> - { this.props.data.pub_url && - + { pub_url && + Open publication. } diff --git a/web/src/utils.js b/web/src/utils.js index bd11698..73226ca 100644 --- a/web/src/utils.js +++ b/web/src/utils.js @@ -265,3 +265,11 @@ export function isNumeric( val ) { return false ; return ! isNaN( val ) ; } + +export function isLink( val ) { + if ( val.substr(0,7) === "http://" || val.substr(0,8) === "https://" ) + return true ; + if ( val.substr(0,7) === "file://" ) + return true ; + return false ; +}