diff --git a/.gitignore b/.gitignore index cc3099d1b..1f5e6b282 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ primo-explore/custom/ primo-explore/tmp/ -.idea/ \ No newline at end of file + +!primo-explore/custom/.gitignore +!primo-explore/tmp/.gitignore + +.idea/ +package-lock.json \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d097cf4e8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, Ex Libris, Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index e7b6e75ed..5f7f2b826 100644 --- a/README.md +++ b/README.md @@ -35,36 +35,34 @@ This custom View folder can be downloaded from your Primo Back Office, by follow ## Installation +Note: If you are not the Administrator of your machine, you might get into problems in the flow below, we recommend using the "Node.js command prompt (search for cmd in your pc to locate it) whenever the instructions below refer to "command line". + 1. Download the project from this repository and place it on your computer 2. Unzip the file you downloaded to a preferred development project folder location -3. Download and install the [Node version 6.9.2](https://nodejs.org/download/release/v6.9.2/) - -4. From command line, run the command : `npm install npm@3.3.12 -g` +3. Download and install the [Node version 16.17.0](https://nodejs.org/download/release/v16.17.0/) -5. Restart your computer +4. Restart your computer -6. From command line, run the command : `npm install -g gulp` +5. From command line, run the command : `npm install -g gulp` -7. In a new command line window, navigate to the project base directory (`cd \path\to\your\project\folder\primo-explore-devenv`) +6. In a new command line window, navigate to the project base directory (`cd \path\to\your\project\folder\primo-explore-devenv`) -8. From command line, run the command : `npm install` (This should install all node modules needed for gulp.) +7. From command line, run the command : `npm install` (This should install all node modules needed for gulp.) ![npm install image](./help_files/npmInstall.png "Running npm install") -9. Edit Gulp configuration file's proxy server setting, found at gulp/config.js : `var PROXY_SERVER = http://your-server:your-port` (Make sure to use your real Sandbox or Production Primo Front-End URL.) Note that for SSL environments (HTTPS) define the server as: `var PROXY_SERVER = https://your-server:443` +8. Edit Gulp configuration file's proxy server setting, found at gulp/config.js : `var PROXY_SERVER = http://your-server:your-port` (Make sure to use your real Sandbox or Production Primo Front-End URL.) Note that for SSL environments (HTTPS) define the server as: `var PROXY_SERVER = https://your-server:443` -10. Populate your custom View package folder in the custom package folder ("...primo-explore\custom"), by either downloading the view code files from your Primo Back Office or using the [primo-explore-package GitHub repository](https://github.com/ExLibrisGroup/primo-explore-package "primo-explore-package repository")) to start a new package folder. (if you have already defined a view package and loaded it to the BO - make sure you download it or else you will not see, and may overwrite, your previous changes.) +9. Populate your custom View package folder in the custom package folder ("...primo-explore\custom"), by either downloading the view code files from your Primo Back Office or using the [primo-explore-package GitHub repository](https://github.com/ExLibrisGroup/primo-explore-package "primo-explore-package repository")) to start a new package folder. (if you have already defined a view package and loaded it to the BO - make sure you download it or else you will not see, and may overwrite, your previous changes.) - If your custom view package folder were to be called "Auto1" then your development environment directory tree should look similar to this: ![Directory tree image](./help_files/direcoryTree.png "Directory tree") - IMPORTANT: The name of your custom view package folder must match an existing view on the proxy server being referenced or the Gulp server will not function properly. For development from scratch, be sure to first create (or copy) a view using the Primo Back Office View Wizard; then you can accomplish your customizations locally using this document. -11. Start your code customizations : - - - From command line, go to your custom view package folder : `cd primo-explore\custom\VIEW_CODE` +10. Start your code customizations : - From command line, run the command : `gulp run --view ` (This will start your local server.) @@ -74,7 +72,7 @@ This custom View folder can be downloaded from your Primo Back Office, by follow - For Primo VE customers, add the --ve flag : `gulp run --view --ve` - Open a browser and type in the following URL : `localhost:8003/primo-explore/?vid=your-view-code` (Example: http://localhost:8003/primo-explore/search?vid=Auto1) - - For Primo VE customers open the following URL : `localhost:8003/discovery/?vid=your-view-code` + - For Primo VE customers open the following URL : `localhost:8003/discovery/?vid=your-institution-code:your-view-code` - Now you should be able to to your customizations with real searches and results, from your previously defined proxy-server. Note: once you start working with this environment, you will discover that the best results are achieved by working in your browser's incognito mode; or you can clear your browser cache before you start the Gulp server. @@ -93,6 +91,8 @@ This custom View folder can be downloaded from your Primo Back Office, by follow - [JavaScript](https://github.com/ExLibrisGroup/primo-explore-package/tree/master/VIEW_CODE/js "javascript documentation") +Note: you have multiple options to edit the css file(custom1.css) and the js file(custom.js), some of them include methods of splitting your developments to seperate files. When using such methods - the custom1.css and custom.js files will be overriden by the different files when gulp is run. Place your custom css and js into files with different names such as custommodule.css or custom.module.js to have it concactinated into the custom css/js files. + ## Publishing packages @@ -113,3 +113,16 @@ Once you finish customizing the package, you can zip up that directory and uploa ![BO Image](./help_files/bo.png "BO up") 5. Don't forget to deploy your changes + + +## Publishing Primo-Studio addons + +Once you finish customizing the package, you can get it ready to be published to Primo-Studio. + +1. In a command line window, navigate to the project base directory : `cd \path\to\your\project\folder\primo-explore-devenv` + +2. From command line, run the command : `gulp prepare-addon` You will be prompted with a menu specifying all of the possible packages you can build. + +3. Once you finished running the script a folder containing the add-on will be created in `\path\to\your\project\folder\primo-explore-devenv\addons`. + +4. From the above folder you can publish your add-on to NPM and to Primo-Studio. For Instructions see: [Primo-Studio add-on tutorial](https://github.com/ExLibrisGroup/Primo-Studio-Addon-Tutorial) diff --git a/addons/.gitignore b/addons/.gitignore new file mode 100644 index 000000000..86d0cb272 --- /dev/null +++ b/addons/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/gulp/config.js b/gulp/config.js index b919f8b12..feb51f2ad 100644 --- a/gulp/config.js +++ b/gulp/config.js @@ -1,185 +1,216 @@ -'use strict'; - -let customFile = 'custom.js'; -let customModuleFile = 'custom.module.js'; -let customCssFile = 'custom1.css'; -let mainFile = 'main.js'; - -let browserify; -let view; -let ve; -let useScss; -let reinstallNodeModules; - - -function setView(_view) { - view = _view; -} - -function setUseScss(_useScss) { - useScss = _useScss; -} - -function getUseScss() { - return useScss; -} - -function setProxy(_proxy) { - this.PROXY_SERVER = _proxy; -} -function getProxy(){ - return PROXY_SERVER; -} -function getVe() { - return ve; -} - -function setVe(_ve) { - ve = _ve; -} -function getBrowserify() { - return browserify; -} - -function setBrowserify(_browserify) { - browserify = _browserify; -} - -function setReinstallNodeModules(_reinstallNodeModules) { - reinstallNodeModules = _reinstallNodeModules; -} - -function getReinstallNodeModules() { - return reinstallNodeModules; -} - -function getView(){ - return view; -} - -function customPath() { - return viewJsDir()+'/'+customFile; -} - -function customModulePath() { - return viewJsDir()+'/'+customModuleFile; -} - -function viewHtmlDir() { - return `primo-explore/custom/${view}/html`; -} - -function viewJsDir() { - return `primo-explore/custom/${view}/js`; -} - -function mainPath() { - return viewJsDir()+'/*.js'; -} - -function mainJsPath() { - return viewJsDir()+'/main.js'; -} - -function customCssMainPath() { - return viewCssDir()+'/*.css'; -} -function customColorsPath(){ - return `colors.json`; -} - -function viewRootDir() { - return `primo-explore/custom/${view}`; -} - -function viewCssDir() { - return `primo-explore/custom/${view}/css`; -} -function customScssDir() { - return `primo-explore/custom/${view}/scss`; -} -function customScssMainPath() { - return customScssDir() + "/main.scss"; -} -function customCssPath() { - return `primo-explore/custom/${view}/css/custom1.css`; -} - -function customNpmModuleRootDir() { - return `primo-explore/custom/${view}/node_modules`; -} - -function customNpmJsCustomPath() { - return `primo-explore/custom/${view}/node_modules/primo-explore*/js/custom.js`; -} - -function customNpmJsModulePath() { - return `primo-explore/custom/${view}/node_modules/primo-explore*/js/custom.module.js`; -} - - -function customNpmJsPath() { - return `primo-explore/custom/${view}/node_modules/primo-explore*/js/*.js`; -} - - -function customNpmCssPath() { - return `primo-explore/custom/${view}/node_modules/primo-explore*/css/*.css`; -} - - - -var SERVERS = { - local: 'http://localhost:8002' -}; - -/** - * The URL to your sandbox or production Primo instance. - * For SSL environments (https), the port number (443) must be included. - * - * Examples: - * var PROXY_SERVER = 'http://abc-primo.hosted.exlibrisgroup.com' - * var PROXY_SERVER = 'https://abc-primo.hosted.exlibrisgroup.com:443' - */ -var PROXY_SERVER = 'http://your-server:your-port'; - - -let buildParams = { - customFile: customFile, - customCssFile: customCssFile, - customPath: customPath, - customModulePath: customModulePath, - mainPath: mainPath, - mainJsPath: mainJsPath, - viewRootDir: viewRootDir, - viewJsDir: viewJsDir, - viewHtmlDir: viewHtmlDir, - viewCssDir: viewCssDir, - customScssDir: customScssDir, - customScssMainPath: customScssMainPath, - customCssPath: customCssPath, - customNpmModuleRootDir: customNpmModuleRootDir, - customNpmJsPath: customNpmJsPath, - customNpmJsCustomPath: customNpmJsCustomPath, - customNpmJsModulePath: customNpmJsModulePath, - customNpmCssPath: customNpmCssPath, - customCssMainPath: customCssMainPath, - customColorsPath: customColorsPath -}; - -module.exports = { - buildParams: buildParams, - PROXY_SERVER: PROXY_SERVER, - setView: setView, - setUseScss: setUseScss, - getUseScss: getUseScss, - setProxy: setProxy, - getReinstallNodeModules: getReinstallNodeModules, - setReinstallNodeModules: setReinstallNodeModules, - proxy: getProxy, - view: getView, - getBrowserify: getBrowserify, - setBrowserify: setBrowserify, - getVe: getVe, - setVe: setVe -}; +'use strict'; + +let customFile = 'custom.js'; +let customModuleFile = 'custom.module.js'; +let customCssFile = 'custom1.css'; +let mainFile = 'main.js'; + +let browserify; +let view; +let ve; +let useScss; +let reinstallNodeModules; +let saml; +let cas; + + +function setView(_view) { + view = _view; +} + +function setSaml(_saml) { + saml = _saml; +} + +function getSaml() { + return saml; +} + +function setCas(_cas) { + cas = _cas; +} + +function getCas() { + return cas; +} + +function setUseScss(_useScss) { + useScss = _useScss; + this.useScss = _useScss; +} + +function getUseScss() { + return useScss; +} + +function setProxy(_proxy) { + this.PROXY_SERVER = _proxy; +} +function getProxy(){ + return PROXY_SERVER; +} +function getVe() { + return ve; +} + +function setVe(_ve) { + ve = _ve; +} +function getBrowserify() { + return browserify; +} + +function setBrowserify(_browserify) { + browserify = _browserify; +} + +function setReinstallNodeModules(_reinstallNodeModules) { + reinstallNodeModules = _reinstallNodeModules; +} + +function getReinstallNodeModules() { + return reinstallNodeModules; +} + +function getView(){ + return view; +} + +function customPath() { + return viewJsDir()+'/'+customFile; +} + +function customModulePath() { + return viewJsDir()+'/'+customModuleFile; +} + +function viewHtmlDir() { + return `primo-explore/custom/${view}/html`; +} + +function viewJsDir() { + return `primo-explore/custom/${view}/js`; +} + +function mainPath() { + return viewJsDir()+'/*.js'; +} + +function mainJsPath() { + return viewJsDir()+'/main.js'; +} + +function customCssMainPath() { + return viewCssDir()+'/*.css'; +} +function customColorsPath(){ + return `colors.json`; +} + +function viewRootDir() { + return `primo-explore/custom/${view}`; +} + +function viewCssDir() { + return `primo-explore/custom/${view}/css`; +} +function customScssDir() { + return `primo-explore/custom/${view}/scss`; +} +function customScssMainPath() { + return customScssDir() + "/main.scss"; +} +function customCssPath() { + return `primo-explore/custom/${view}/css/custom1.css`; +} + +function customNpmModuleRootDir() { + return `primo-explore/custom/${view}/node_modules`; +} + +function customNpmJsCustomPath() { + return `primo-explore/custom/${view}/node_modules/primo-explore*/js/custom.js`; +} + +function customNpmJsModulePath() { + return `primo-explore/custom/${view}/node_modules/primo-explore*/js/custom.module.js`; +} + + +function customNpmJsPath() { + return `primo-explore/custom/${view}/node_modules/primo-explore*/js/*.js`; +} + +function customNpmDistPath() { + return `primo-explore/custom/${view}/node_modules/primo-explore*/dist/*.js`; +} + + +function customNpmCssPath() { + return `primo-explore/custom/${view}/node_modules/primo-explore*/css/*.css`; +} + +function customNpmHtmlPath() { + return `primo-explore/custom/${view}/node_modules/primo-explore*/html/*.html`; +} + +var SERVERS = { + local: 'http://localhost:8002' +}; + +/** + * The URL to your production Primo instance. + * For SSL environments (https), the port number (443) must be included. + * + * Examples: + * var PROXY_SERVER = 'http://abc-primo.hosted.exlibrisgroup.com' + * var PROXY_SERVER = 'https://abc-primo.hosted.exlibrisgroup.com:443' + */ +var PROXY_SERVER = 'http://your-server:your-port'; + + +let buildParams = { + customFile: customFile, + customCssFile: customCssFile, + customPath: customPath, + customModulePath: customModulePath, + mainPath: mainPath, + mainJsPath: mainJsPath, + viewRootDir: viewRootDir, + viewJsDir: viewJsDir, + viewHtmlDir: viewHtmlDir, + viewCssDir: viewCssDir, + customScssDir: customScssDir, + customScssMainPath: customScssMainPath, + customCssPath: customCssPath, + customNpmModuleRootDir: customNpmModuleRootDir, + customNpmJsPath: customNpmJsPath, + customNpmDistPath: customNpmDistPath, + customNpmJsCustomPath: customNpmJsCustomPath, + customNpmJsModulePath: customNpmJsModulePath, + customNpmCssPath: customNpmCssPath, + customNpmHtmlPath: customNpmHtmlPath, + customCssMainPath: customCssMainPath, + customColorsPath: customColorsPath +}; + +module.exports = { + buildParams: buildParams, + PROXY_SERVER: PROXY_SERVER, + setView: setView, + setUseScss: setUseScss, + getUseScss: getUseScss, + setProxy: setProxy, + getReinstallNodeModules: getReinstallNodeModules, + setReinstallNodeModules: setReinstallNodeModules, + proxy: getProxy, + view: getView, + getBrowserify: getBrowserify, + setBrowserify: setBrowserify, + getVe: getVe, + setVe: setVe, + getSaml: getSaml, + setSaml: setSaml, + getCas: getCas, + setCas: setCas +}; diff --git a/gulp/primoProxy.js b/gulp/primoProxy.js index bd492a198..05a5145b4 100644 --- a/gulp/primoProxy.js +++ b/gulp/primoProxy.js @@ -1,204 +1,208 @@ -var modRewrite = require('connect-modrewrite'); -var fs = require('fs') -var Promise = require("bluebird"); -var concat = require('concat-stream'); -var config = require('./config'); -var glob = require('glob'); -Promise.promisifyAll(glob); -var Response = require('http-response-object'); - -module.exports.getCustimazationObject = function (vid,appName) { - - - var basedir = appName+'/custom'; - var ignored = ['img', 'css', 'custom']; - var base_path = 'custom/'; - var customizationObject = { - viewJs: '', - centralJs: '', - viewCss: '', - centralCss: '', - favIcon: '', - libraryLogo: '', - resourceIcons: '', - staticHtml: '' - }; - - var promises = []; - var packages = glob.sync(base_path + "*", {cwd:appName,ignore:'**/README.md'}); - - var isInherited = packages.indexOf(base_path + 'CENTRAL_PACKAGE') > -1; - if(vid !== ''){ - var viewPackage = base_path + vid; - } - if(vid === '') { - var viewPackage = packages.filter((package) => package !== base_path + 'CENTRAL_PACKAGE'); - } - - - viewPackage = viewPackage || viewPackage[0]; - console.log(viewPackage); - if(viewPackage.length ===0 ){ - viewPackage = ''; - } - //js - - if(viewPackage !== '' && viewPackage !== 'CENTRAL_PACKAGE') { - customizationObject.viewJs = glob.sync(viewPackage + "/js/custom.js", {cwd: appName}); - } - if (isInherited) { - customizationObject.centralJs = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/js/custom.js", {cwd:appName}); - } - - //css - - - customizationObject.viewCss = glob.sync(viewPackage + "/css/custom1.css", {cwd:appName}); - - if (isInherited) { - customizationObject.centralCss = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/css/custom1.css", {cwd:appName}); - } - - //images - - customizationObject.favIcon = glob.sync(viewPackage + "/img/favicon.ico", {cwd:appName}); - - - if (isInherited && customizationObject.favIcon === '') { - customizationObject.favIcon = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/img/favicon.ico", {cwd:appName}) - } - customizationObject.libraryLogo = glob.sync(viewPackage + "/img/library-logo.png", {cwd:appName})[0]; - if (isInherited && (!customizationObject.libraryLogo || customizationObject.libraryLogo === '')) { - customizationObject.libraryLogo = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/img/library-logo.png", {cwd:appName})[0]; - } - - var paths = glob.sync(viewPackage + "/img/icon_**.png", {cwd:appName}); - customizationObject.resourceIcons = {}; - for (path of paths) { - var pathFixed = path.substring(path.indexOf('/img/icon_') + 10, path.indexOf('.png')); - customizationObject.resourceIcons[pathFixed] = path; - } - - - if (isInherited) { - var paths = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/img/icon_**.png", {cwd:appName}); - - for (path of paths) { - var pathFixed = path.substring(path.indexOf('/img/icon_') + 10, path.indexOf('.png')); - if (!customizationObject.resourceIcons[pathFixed]) { - customizationObject.resourceIcons[pathFixed] = path; - } - } - - - } - - - //html - var paths = glob.sync(viewPackage + "/html/home_**.html", {cwd:appName}); - - if(paths && paths.length > 0){ // for August 2016 version - customizationObject.staticHtml = {}; - customizationObject.staticHtml.homepage = {}; - for (path of paths) { - - var pathFixed = path.substring(path.indexOf('/html/home_')+11, path.indexOf('.html')); - customizationObject.staticHtml.homepage[pathFixed] = path; - } - - - if (isInherited) { - var paths = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/html/home_**.html", {cwd:appName}); - - for (path of paths) { - var pathFixed = path.substring(path.indexOf('/html/home_')+11, path.indexOf('.html')); - if (!customizationObject.staticHtml.homepage[pathFixed]) { - customizationObject.staticHtml.homepage[pathFixed] = path; - } - - } - - - } - - }else{ // starting November 2016 version - var paths = glob.sync(viewPackage + "/html/**/*.html", {cwd:appName}); - if(!paths || paths.length ===0){ - paths = glob.sync(viewPackage + "/html/*.html", {cwd:appName}); - } - var staticHtmlRes = {}; - staticHtmlRes = getHtmlCustomizations(paths,viewPackage,staticHtmlRes); - - if (isInherited) { - var paths = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/html/**/*.html", {cwd:appName}); - staticHtmlRes = getHtmlCustomizations(paths,'custom/CENTRAL_PACKAGE',staticHtmlRes); - } - customizationObject.staticHtml = staticHtmlRes; - } - function getLanguage(entry) { - var start = entry.indexOf('.html')-5; - var res = entry.substring(start,start+5); - return res; - } - function getHtmlCustomizations(paths,path,staticDict){ - var patternString = path+'/html/'; - - var re = new RegExp(patternString, "g"); - var res = paths - .map(e => e.replace(re,'')); - - - res.forEach((e)=> { - var lang = getLanguage(e); - var dirName = e.replace('_'+lang+'.html',''); - if(!staticDict[dirName]) { - staticDict[dirName] = {}; - } - staticDict[dirName][lang] = path+ '/html/'+e; - if(lang ==='en_US') { - staticDict[dirName]['default'] = path+ '/html/'+e; - } - - - }); - - return staticDict; - } - - - - - - - - return customizationObject; - - -} - - -module.exports.proxy_function = function () { - var proxyServer = config.PROXY_SERVER; - var res = new Response(200, {'content-type': 'text/css'}, new Buffer(''), ''); - - - - - return modRewrite([ - '/primo_library/libweb/webservices/rest/(.*) ' + proxyServer + '/primo_library/libweb/webservices/rest/$1 [PL]', - '/primaws/rest/(.*) ' + proxyServer + '/primaws/rest/$1 [PL]', - '/primo_library/libweb/primoExploreLogin ' + proxyServer + '/primo_library/libweb/primoExploreLogin [PL]', - '/primaws/suprimaLogin ' + proxyServer + '/primaws/suprimaLogin [PL]', - - '/primo-explore/index.html ' + proxyServer + '/primo-explore/index.html [PL]', - '/discovery/index.html ' + proxyServer + '/discovery/index.html [PL]', - '/primo-explore/custom/(.*) /custom/$1 [L]', - '/discovery/custom/(.*) /custom/$1 [L]', - '/primo-explore/(.*) ' + proxyServer + '/primo-explore/$1 [PL]', - '/discovery/(.*) ' + proxyServer + '/discovery/$1 [PL]', - '.*primoExploreJwt=.* /index.html [L]', - '^[^\\.]*$ /index.html [L]' - ]); - - -}; +var modRewrite = require('connect-modrewrite'); +var fs = require('fs') +var Promise = require("bluebird"); +var config = require('./config'); +var glob = require('glob'); +Promise.promisifyAll(glob); +var Response = require('http-response-object'); + +module.exports.getCustimazationObject = function (vid,appName) { + + + var basedir = appName+'/custom'; + var ignored = ['img', 'css', 'custom']; + var base_path = 'custom/'; + var customizationObject = { + viewJs: '', + centralJs: '', + viewCss: '', + centralCss: '', + favIcon: '', + libraryLogo: '', + resourceIcons: '', + staticHtml: '' + }; + + var promises = []; + var packages = glob.sync(base_path + "*", {cwd:appName,ignore:'**/README.md'}); + + var isInherited = packages.indexOf(base_path + 'CENTRAL_PACKAGE') > -1; + if(vid !== ''){ + var viewPackage = base_path + vid; + } + if(vid === '') { + var viewPackage = packages.filter((package) => package !== base_path + 'CENTRAL_PACKAGE'); + } + + + viewPackage = viewPackage || viewPackage[0]; + console.log(viewPackage); + if(viewPackage.length ===0 ){ + viewPackage = ''; + } + //js + + if(viewPackage !== '' && viewPackage !== 'CENTRAL_PACKAGE') { + customizationObject.viewJs = glob.sync(viewPackage + "/js/custom.js", {cwd: appName}); + } + if (isInherited) { + customizationObject.centralJs = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/js/custom.js", {cwd:appName}); + } + + //css + + + customizationObject.viewCss = glob.sync(viewPackage + "/css/custom1.css", {cwd:appName}); + + if (isInherited) { + customizationObject.centralCss = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/css/custom1.css", {cwd:appName}); + } + + //images + + customizationObject.favIcon = glob.sync(viewPackage + "/img/favicon.ico", {cwd:appName}); + + + if (isInherited && customizationObject.favIcon === '') { + customizationObject.favIcon = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/img/favicon.ico", {cwd:appName}) + } + customizationObject.libraryLogo = glob.sync(viewPackage + "/img/library-logo.png", {cwd:appName})[0]; + if (isInherited && (!customizationObject.libraryLogo || customizationObject.libraryLogo === '')) { + customizationObject.libraryLogo = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/img/library-logo.png", {cwd:appName})[0]; + } + + var paths = glob.sync(viewPackage + "/img/icon_**.png", {cwd:appName}); + customizationObject.resourceIcons = {}; + for (path of paths) { + var pathFixed = path.substring(path.indexOf('/img/icon_') + 10, path.indexOf('.png')); + customizationObject.resourceIcons[pathFixed] = path; + } + + + if (isInherited) { + var paths = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/img/icon_**.png", {cwd:appName}); + + for (path of paths) { + var pathFixed = path.substring(path.indexOf('/img/icon_') + 10, path.indexOf('.png')); + if (!customizationObject.resourceIcons[pathFixed]) { + customizationObject.resourceIcons[pathFixed] = path; + } + } + + + } + + + //html + var paths = glob.sync(viewPackage + "/html/home_**.html", {cwd:appName}); + + if(paths && paths.length > 0){ // for August 2016 version + customizationObject.staticHtml = {}; + customizationObject.staticHtml.homepage = {}; + for (path of paths) { + + var pathFixed = path.substring(path.indexOf('/html/home_')+11, path.indexOf('.html')); + customizationObject.staticHtml.homepage[pathFixed] = path; + } + + + if (isInherited) { + var paths = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/html/home_**.html", {cwd:appName}); + + for (path of paths) { + var pathFixed = path.substring(path.indexOf('/html/home_')+11, path.indexOf('.html')); + if (!customizationObject.staticHtml.homepage[pathFixed]) { + customizationObject.staticHtml.homepage[pathFixed] = path; + } + + } + + + } + + }else{ // starting November 2016 version + var paths = glob.sync(viewPackage + "/html/**/*.html", {cwd:appName}); + if(!paths || paths.length ===0){ + paths = glob.sync(viewPackage + "/html/*.html", {cwd:appName}); + } + var staticHtmlRes = {}; + staticHtmlRes = getHtmlCustomizations(paths,viewPackage,staticHtmlRes); + + if (isInherited) { + var paths = glob.sync(base_path + 'CENTRAL_PACKAGE' + "/html/**/*.html", {cwd:appName}); + staticHtmlRes = getHtmlCustomizations(paths,'custom/CENTRAL_PACKAGE',staticHtmlRes); + } + customizationObject.staticHtml = staticHtmlRes; + } + function getLanguage(entry) { + var numberOfCharsForLang = config.getVe() ? 2 : 5; + var start = entry.indexOf('.html')-numberOfCharsForLang; + var res = entry.substring(start,start+numberOfCharsForLang); + return res; + } + function getHtmlCustomizations(paths,path,staticDict){ + var patternString = path+'/html/'; + + var re = new RegExp(patternString, "g"); + var res = paths + .map(e => e.replace(re,'')); + + + res.forEach((e)=> { + var lang = getLanguage(e); + var dirName = e.replace('_'+lang+'.html',''); + if (dirName.indexOf('/') > -1) { + var sepIndex = dirName.indexOf('/'); + dirName = dirName.substr(0, sepIndex); + } + if(!staticDict[dirName]) { + staticDict[dirName] = {}; + } + staticDict[dirName][lang] = path+ '/html/'+e; + if(lang ==='en_US' || lang === 'en') { + staticDict[dirName]['default'] = path+ '/html/'+e; + } + + + }); + + return staticDict; + } + + + + + + + + return customizationObject; + + +} + + +module.exports.proxy_function = function () { + var proxyServer = config.PROXY_SERVER; + var res = new Response(200, {'content-type': 'text/css'}, Buffer.from(''), ''); + var loginRewriteFlags = (config.getSaml() || config.getCas()) ? 'RL' : 'PL'; + + return modRewrite([ + '/view/action/(.*) ' + proxyServer + '/view/action/$1 [PL]', + '/primo_library/libweb/webservices/rest/(.*) ' + proxyServer + '/primo_library/libweb/webservices/rest/$1 [PL]', + '/primaws/rest/(.*) ' + proxyServer + '/primaws/rest/$1 [PL]', + '/primo_library/libweb/primoExploreLogin ' + proxyServer + '/primo_library/libweb/primoExploreLogin [' + loginRewriteFlags + ']', + '/primaws/suprimaLogin ' + proxyServer + '/primaws/suprimaLogin [' + loginRewriteFlags + ']', + '/primaws/suprimaExtLogin ' + proxyServer + '/primaws/suprimaExtLogin [' + loginRewriteFlags + ']', + + '/primo-explore/index.html ' + proxyServer + '/primo-explore/index.html [PL]', + '/discovery/index.html ' + proxyServer + '/discovery/index.html [PL]', + '/primo-explore/custom/(.*) /custom/$1 [L]', + '/discovery/custom/(.*) /custom/$1 [L]', + '/primo-explore/(.*) ' + proxyServer + '/primo-explore/$1 [PL]', + '/discovery/(.*) ' + proxyServer + '/discovery/$1 [PL]', + '.*primoExploreJwt=.* /index.html [L]', + '^[^\\.]*$ /index.html [L]' + ]); + + +}; diff --git a/gulp/tasks/00-select-view.js b/gulp/tasks/00-select-view.js new file mode 100644 index 000000000..2ad99e222 --- /dev/null +++ b/gulp/tasks/00-select-view.js @@ -0,0 +1,56 @@ +'use strict'; +const gulp = require('gulp'); +const glob = require('glob'); +const prompt = require('prompt'); +const zip = require('gulp-zip'); +const config = require('../config.js'); + +gulp.task('select-view', (cb) => { + const basedir = 'primo-explore/custom/'; + const customFolderExp = basedir + '*/'; + const files = glob.sync(customFolderExp, {}); + + return new Promise(resolve => { + if (!config.view()) { + console.log('Please Choose a view to use:\r\n'); + files.forEach(function(element, index, array){ + console.log(index+1 + ': '+ element.replace(basedir,'').replace('/','')); + console.log('\r\n'); + }); + + prompt.start(); + const property = { + name: 'view', + message: 'Please Choose view to use' + }; + prompt.get(property, function (err, result) { + console.log('\r\n'); + let code = result.view; + + if(files[result.view - 1]){ + code = files[result.view - 1].replace(basedir,'').replace('/',''); + } + config.setView(code); + resolve(); + }); + } else { + let valid = false + for (let index in files) { + let dir = files[index].replace(basedir,'').replace('/','') + + if(dir === config.view()) { + valid = true + break; + } + } + + if (!valid) { + resolve() + cb("--view must be a valid view") + } else { + resolve() + } + } + }) +}) + diff --git a/gulp/tasks/buildCustomHtmlTemplates.js b/gulp/tasks/01-custom-html-templates.js similarity index 50% rename from gulp/tasks/buildCustomHtmlTemplates.js rename to gulp/tasks/01-custom-html-templates.js index f7026b23f..d1ba5d2e7 100644 --- a/gulp/tasks/buildCustomHtmlTemplates.js +++ b/gulp/tasks/01-custom-html-templates.js @@ -20,22 +20,35 @@ function parseModuleName(){ function prepareTempltesWithBrowserify(){ let module = parseModuleName(); - gulp.src(buildParams.viewHtmlDir() + '/templates/**/*.html') - .pipe(templateCache({filename:'customTemplates.js', module: module})) + return gulp.src(buildParams.viewHtmlDir() + '/templates/**/*.html') + .pipe(templateCache({ + filename:'customTemplates.js', + module: module, + transformUrl: function(url) { + return url.replace(/^\/+/g, ''); + } + })) .pipe(gulp.dest(buildParams.viewJsDir())); } function prepareTemplates() { if(config.getBrowserify()){ - prepareTempltesWithBrowserify(); + return prepareTempltesWithBrowserify(); } else{ - gulp.src(buildParams.viewHtmlDir() + '/templates/**/*.html') - .pipe(templateCache({filename:'customTemplates.js', templateHeader: 'app.run(function($templateCache) {', templateFooter: '});'})) + return gulp.src([buildParams.viewHtmlDir() + '/templates/**/*.html', buildParams.customNpmHtmlPath()]) + .pipe(templateCache({ + filename:'customTemplates.js', + templateHeader: 'app.run(function($templateCache) {', + templateFooter: '});', + transformUrl: function(url) { + return url.replace(/^\/+/g, ''); + } + })) .pipe(gulp.dest(buildParams.viewJsDir())); } } -gulp.task('custom-html-templates', () => { - prepareTemplates(); -}) +gulp.task('custom-html-templates', gulp.series('select-view', (cb) => { + prepareTemplates().on('end', cb); +})) diff --git a/gulp/tasks/02-custom-js.js b/gulp/tasks/02-custom-js.js new file mode 100644 index 000000000..d242b2eef --- /dev/null +++ b/gulp/tasks/02-custom-js.js @@ -0,0 +1,93 @@ +'use strict'; + +const gulp = require('gulp'); +const babel = require('gulp-babel'); +const config = require('../config.js'); +const concat = require("gulp-concat"); +const wrap = require("gulp-wrap"); +const gutil = require('gulp-util'); +const browserify = require("browserify"); +const source = require('vinyl-source-stream'); +const uglify = require('gulp-uglify'); +const buffer = require('vinyl-buffer'); +const sourcemaps = require('gulp-sourcemaps'); + +let buildParams = config.buildParams; + + + + +gulp.task('watch-js', gulp.series('select-view', (cb) => { + gulp.watch([`${buildParams.viewJsDir()}/**/*.js`,'!'+buildParams.customPath()], {interval: 1000, usePolling: true}, gulp.series('custom-js')); + cb(); +})); + + +gulp.task('custom-js', gulp.series('select-view', 'custom-html-templates',(cb) => { + if (config.getBrowserify()) { + buildByBrowserify().on('end', cb); + } + else { + buildByConcatination().on('end', cb); + } +})); + +function getBrowserifyBabelPlugins() { + return [ + "transform-html-import-to-string", ["angularjs-annotate", { "explicitOnly" : true}] + ]; +} + +function getDefaultBabelPlugins() { + return [ + ["transform-define", { + "process.env.NODE_ENV": process.env.NODE_ENV, + }] + ]; +} + +const getBabelConfig = () => { + return ({ + presets: ["es2015"], + plugins: getDefaultBabelPlugins().concat(config.getBrowserify() ? getBrowserifyBabelPlugins() : []), + sourceMaps: config.getBrowserify(), + }); +} + +function buildByConcatination() { + return gulp.src([buildParams.customModulePath(),buildParams.mainPath(),buildParams.customNpmJsPath(),buildParams.customNpmDistPath(),'!'+buildParams.customPath(),'!'+buildParams.customNpmJsModulePath(),'!'+buildParams.customNpmJsCustomPath()],{allowEmpty:true}) + .pipe(concat(buildParams.customFile)) + .pipe(babel(getBabelConfig())) + .on("error", function(err) { + if (err && err.codeFrame) { + gutil.log( + gutil.colors.red("Browserify error: "), + gutil.colors.cyan(err.filename) + ` [${err.loc.line},${err.loc.column}]`, + "\r\n" + err.message + "\r\n" + err.codeFrame); + } + else { + gutil.log(err); + } + this.emit("end"); + }) + .pipe(wrap('(function(){\n"use strict";\n<%= contents %>\n})();')) + .pipe(gulp.dest(buildParams.viewJsDir())); +} + +function buildByBrowserify() { + return browserify({ + debug: true, + entries: buildParams.mainJsPath(), + paths:[ + buildParams.viewJsDir()+'/node_modules' + ] + }) + .transform("babelify", getBabelConfig()) + .bundle() + .pipe(source(buildParams.customFile)) + .pipe(buffer()) + .pipe(sourcemaps.init({loadMaps: true})) + .pipe(process.env.NODE_ENV === 'production' ? uglify() : gutil.noop()) + .pipe(sourcemaps.write('./')) + .pipe(gulp.dest(buildParams.viewJsDir())); +} diff --git a/gulp/tasks/build-scss.js b/gulp/tasks/03-scss.js similarity index 76% rename from gulp/tasks/build-scss.js rename to gulp/tasks/03-scss.js index dce92cbd6..ff87f09de 100644 --- a/gulp/tasks/build-scss.js +++ b/gulp/tasks/03-scss.js @@ -3,6 +3,7 @@ let autoprefixer = require('gulp-autoprefixer'); let config = require('../config').buildParams; let useScss = require('../config').getUseScss; +let isVe = require('../config').getVe; let proxy_server = require('../config').PROXY_SERVER let gulp = require('gulp'); let cssnano = require('gulp-cssnano'); @@ -22,7 +23,7 @@ let stylesBaseDir = 'www/styles/partials'; let templateFile = stylesBaseDir+'/_variables.tmpl.scss'; let OTBColorsFile = stylesBaseDir+'/../colors.json'; let scssFile = '_variables.scss'; -var runSequence = require('run-sequence'); +var runSequence = require('gulp4-run-sequence'); let fs = require('fs'); let del = require('del'); let lodashMerge = require('lodash/merge'); @@ -31,16 +32,27 @@ let gutil = require('gulp-util'); gulp.task('cleanup',()=> del(['www'])); gulp.task('extract-scss-files', ()=> { - let proxy_server = require('../config').PROXY_SERVER; - console.log(proxy_server+'/primo-explore/lib/scsss.tar.gz'); - let url = proxy_server+'/primo-explore/lib/scsss.tar.gz'; + let proxy_server = require('../config').PROXY_SERVER; + let prefix; + if (isVe()) { + prefix = '/discovery'; + } else { + prefix = '/primo-explore'; + } + let url = proxy_server+prefix+'/lib/scsss.tar.gz'; + console.log(url); var headers = { /*'Accept-Encoding': 'gzip'*/ }; return request({url:url, 'headers': headers}) .pipe(zlib.createGunzip()) // unzip - .pipe(tar.extract('.')) + .pipe(tar.extract('.', {map: (header)=>{ + if (header.name.indexOf('src/main/webapp') > -1){ + header.name = header.name.replace('src/main/webapp', 'www'); + } + return header; + }})); }); gulp.task('color-variables',() => { let colorVariables = JSON.parse(fs.readFileSync(config.viewCssDir() + '/../colors.json', 'utf8')); @@ -63,8 +75,7 @@ gulp.task('compile-scss',() => { })) // .pipe(sourcemaps.init()) .pipe(sass()) - .pipe(autoprefixer({ - browsers: ['last 2 versions'], + .pipe(autoprefixer({ cascade: false })); let colorStream = allCss @@ -79,7 +90,7 @@ gulp.task('compile-scss',() => { }); gulp.task('app-css', (cb) => { - runSequence('extract-scss-files','color-variables', 'compile-scss', 'cleanup', cb); + runSequence('extract-scss-files','color-variables', 'compile-scss', 'cleanup', cb); }); /** @@ -88,13 +99,14 @@ gulp.task('app-css', (cb) => { * Please note. The logic of this task will only execute if the run task is * executed with the "useScss" parameter, e.g.: gulp run --view UNIBZ --useScss */ -gulp.task("watch-custom-scss", () => { +gulp.task("watch-custom-scss", gulp.series('select-view', (cb) => { if (!useScss()) { - return; + cb(); + return; } - - gulp.watch([config.customScssDir() + "/**/*.scss"], ["custom-scss"]); -}); + gulp.watch([config.customScssDir() + "/**/*.scss"], {interval: 1000, usePolling: true}, gulp.series('custom-scss')); + cb(); +})); /** * Compiles the custom scss to a css file called custom-scss-compiled.css which @@ -105,14 +117,15 @@ gulp.task("watch-custom-scss", () => { * Please note. The logic of this task will only execute if the run task is * executed with the "useScss" parameter, e.g.: gulp run --view UNIBZ --useScss */ -gulp.task("custom-scss", () => { +gulp.task("custom-scss", gulp.series('select-view', (cb) => { if (!useScss()) { + cb(); return; } gutil.log("Start Creating custom CSS from custom SCSS"); - let customScss = gulp.src(config.customScssMainPath()) + let customScss = gulp.src(config.customScssMainPath(),{allowEmpty:true}) .pipe(plumber({ errorHandler: function (err) { console.log('1111111' + err); @@ -122,13 +135,12 @@ gulp.task("custom-scss", () => { // .pipe(sourcemaps.init()) .pipe(sass()) .pipe(autoprefixer({ - browsers: ['last 2 versions'], cascade: false })) .pipe(rename("custom-scss-compiled.css")) .pipe(gulp.dest(config.viewCssDir())); gutil.log("End Creating custom CSS from custom SCSS"); - + cb(); return customScss; -}); +})); diff --git a/gulp/tasks/buildCustomCss.js b/gulp/tasks/04-custom-css.js similarity index 70% rename from gulp/tasks/buildCustomCss.js rename to gulp/tasks/04-custom-css.js index b007a98ed..927dae71d 100644 --- a/gulp/tasks/buildCustomCss.js +++ b/gulp/tasks/04-custom-css.js @@ -11,19 +11,21 @@ var glob = require('glob'); let buildParams = config.buildParams; -gulp.task('watch-css', () => { - gulp.watch([buildParams.customCssMainPath(),buildParams.customNpmCssPath(),'!'+buildParams.customCssPath()],['custom-css']); -}); +gulp.task('watch-css', gulp.series('select-view', (cb) => { + gulp.watch([buildParams.customCssMainPath(),buildParams.customNpmCssPath(),'!'+buildParams.customCssPath()], {interval: 1000, usePolling: true}, gulp.series('custom-css')); + cb(); +})); -gulp.task('custom-css', () => { - + +gulp.task('custom-css', gulp.series('select-view', () => { + return gulp.src([buildParams.customCssMainPath(),buildParams.customNpmCssPath(),'!'+buildParams.customCssPath()]) .pipe(concat(buildParams.customCssFile)) .pipe(gulp.dest(buildParams.viewCssDir())); - -}); \ No newline at end of file + +})); diff --git a/gulp/tasks/cssColors.js b/gulp/tasks/05-css-colors.js similarity index 100% rename from gulp/tasks/cssColors.js rename to gulp/tasks/05-css-colors.js diff --git a/gulp/tasks/06-prepare-addon.js b/gulp/tasks/06-prepare-addon.js new file mode 100644 index 000000000..171812ef5 --- /dev/null +++ b/gulp/tasks/06-prepare-addon.js @@ -0,0 +1,295 @@ +'use strict'; +const gulp = require('gulp'); +const config = require('../config.js'); +const spawn = require('child_process').spawn; +const fs = require('fs'); +const Promise = require('bluebird'); +const prompt = require('prompt'); +const camelCase = require('camel-case'); +const streamToPromise = require('stream-to-promise'); +const colors = require('colors/safe'); +const path = require('path'); + + +let buildParams = config.buildParams; + +gulp.task('prepare-addon', gulp.series('select-view', 'custom-js','custom-scss','custom-css', function() { + let view = config.view(); + let packageJsonPath = buildParams.viewRootDir() + '/package.json'; + let npmId; + let directoryName; + let hookName; + + let runNpmInitIfNeeded = new Promise((resolve, reject) => { + if (!fs.existsSync(packageJsonPath)) { + let childProcess = spawn('npm', ['init'], {cwd: buildParams.viewRootDir(), shell: true, stdio: 'inherit'}); + + childProcess.on('error', err => { + reject(err); + }); + + childProcess.on('exit', (code, signal) => { + if (!code) { + resolve(); + } + }); + } else { + resolve(); + } + }); + + return runNpmInitIfNeeded + .then(findNpmIdInPackageJson, handleError) + .then(makeDirectory, handleError) + .then(copyFiles, handleError) + .then(compileAddon, handleError) + .then(createDescriptorJson, handleError) + .then(announceFinishedProcess, handleError); + + + function findNpmIdInPackageJson() { + return new Promise((resolve, reject) => { + fs.exists(packageJsonPath, exists => { + if (exists) { + fs.readFile(packageJsonPath, (err, data) => { + if (err) { + reject(err); + } + let packageJson = JSON.parse(data.toString()); + npmId = camelCase(packageJson.name); + resolve(); + }); + } + }); + }); + } + + + function makeDirectory() { + return new Promise((resolve, reject) => { + directoryName = './addons/' + npmId; + fs.mkdir(directoryName, err => reject(err)); + resolve(); + }) + } + + + function copyFiles() { + return streamToPromise(gulp.src(['./primo-explore/custom/' + view + '/package*.json', './primo-explore/custom/' + view + '/html/**', './primo-explore/custom/' + view + '/img/**', './primo-explore/custom/' + view + '/css/custom1.css', './primo-explore/custom/' + view + '/js/custom.js'], {base: './primo-explore/custom/' + view}) + .pipe(gulp.dest(directoryName))); + } + + + function compileAddon() { + return new Promise((resolve, reject) => { + let customJsPath = directoryName + '/js/' + buildParams.customFile; + if (fs.existsSync(customJsPath)) { + fs.readFile(customJsPath, (err, data) => { + if (err) { + reject(err); + } + let dataString = data.toString(); + + // remove comments + dataString = dataString.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, ''); + + // group 1 = component name, group 2 = controller name if exists controller + let componentRegex = /\.component[\s]*?\([\s]*?['"](prm.*?After)['"](?:(?:(?!\.component)[\s\S])*?controller[\s]*?:[\s]*?['"](.*)['"])?/g; + + if (dataString.match(componentRegex).length > 1) { + let arr = []; + let match; + while (match = componentRegex.exec(dataString)) { + arr.push(match[1]); + } + reject(new Error('Only one Primo hook allowed, you tried to use several: ' + arr.toString())); + } + + let match = componentRegex.exec(dataString); + + if (match) { + hookName = match[1]; + let controllerName = match[2]; // may be null + + dataString = dataString.replace(new RegExp(controllerName || '', 'g'), npmId + 'Controller'); + dataString = dataString.replace(new RegExp(hookName, 'g'), npmId); + } + + // remove wrapping function + let wrappingFunctionRegex = /\s*?\(\s*?function\s*?\(\s*?\)\s*?{\s*(?:["']use strict["'];\s*)?([\s\S]*)\s*?}\s*?\)\s*?\(\s*?\)\s*?;?/g; + if (match = wrappingFunctionRegex.exec(dataString)) { + dataString = dataString.replace(wrappingFunctionRegex, match[1]); + } + + // remove app declaration + // group 1 = app variable name + let appDeclarationViewOrCentralCustomRegex = /[\s]*(?:var|let)?[\s]*(.*?)[\s]*=[\s]*angular[\s]*\.module\([\s]*['"](?:view|central)Custom['"][\s]*,[\s]*\[['"]angularLoad['"]\]\);/g; + if (match = appDeclarationViewOrCentralCustomRegex.exec(dataString)) { + // remove matched line + dataString = dataString.replace(appDeclarationViewOrCentralCustomRegex, ''); + + // change all the occurrences of the variable to 'app' + let variableName = match[1]; + if (variableName !== 'app') { + let variableUseRegex = new RegExp('([[=(,]\\s*?)' + variableName + '([^\\w])|([^\\w])' + variableName + '(\\s*?[.)|\\]}=;])', 'gm'); + dataString = dataString.replace(variableUseRegex, '$1$3app$2$4'); + } + } + + // remove constant config if exists + // group 1 = config name + let studioConfigDeclerationRegex = /\.(?:constant|value|service|factory)[\s]*?\([\s]*['"](.*?StudioConfig)/g; + if (dataString.match(studioConfigDeclerationRegex)) { + dataString = dataString.replace(studioConfigDeclerationRegex, '$&DevenvTest'); + } + + // group 1 = module name + let moduleRegex = /\.module[\s]*?\([\s]*((?:').*?(?:')|(?:").*?(?:")|.*?)[\s]*?[),]/g; + + // push all modules + while (match = moduleRegex.exec(dataString)) { + dataString = dataString + '\napp.requires.push(' + match[1] + ');'; + } + + // write content to {{npmId}}.js + let wstream = fs.createWriteStream(directoryName + '/js/' + npmId + '.js'); + wstream.write(dataString); + wstream.end(); + + // delete custom.js file + fs.unlink(customJsPath, err1 => { + if (err1) { + reject(err1); + } + }); + + resolve(); + + }); + } else { + resolve(); + } + }); + } + + + function createDescriptorJson() { + return new Promise((resolve, reject) => { + let npmignorePath = directoryName + '/.npmignore'; + let descriptorJsonFileName = 'descriptor.json'; + let descriptorJsonPath = directoryName + '/' + descriptorJsonFileName; + + console.log("Creating '.npmignore' file\n"); + + // create .npmignore file with descriptor.json + let wstream = fs.createWriteStream(npmignorePath); + wstream.write(descriptorJsonFileName + '\n'); + wstream.end(); + + console.log("Creating 'descriptor.json' file\n"); + + // needed values for descriptor.json: face (image), notes (description), who (author), what (title), linkGit, npmid, version, hook + let descriptor = {'face': '', 'notes': '', 'who': '', 'what': '', 'linkGit': '', 'npmid': '', 'version': '', 'hook': hookName}; + + // get known values from package.json + fs.readFile(packageJsonPath, (err, data) => { + if (err) { + reject(err); + } + let packageJson = JSON.parse(data.toString()); + descriptor.notes = packageJson.description; + descriptor.who = packageJson.author.name || packageJson.author; // maybe empty, but is string + if (packageJson.repository) { + descriptor.linkGit = packageJson.repository.url || packageJson.repository; // maybe null + } + descriptor.npmid = packageJson.name; + descriptor.version = packageJson.version; + + // until here have: notes (description), who (author) {maybe}, linkGit {maybe}, npmid, version, hook + // need to ask for: face (image), who (author) {maybe}, what (title), linkGit {maybe} + + // ask for values not exists already + console.log("This utility will walk you through creating a 'descriptor.json' file.\n" + + "It only covers the most common items, and tries to guess sensible defaults."); + prompt.start(); + prompt.colors = false; + prompt.message = ''; + let properties = [ + { + name: 'what', + message: 'Enter title', + required: true, + default: descriptor.npmid + }, { + name: 'face', + message: 'Enter a link to photo of you', + required: true + } + ]; + + if (!descriptor.who) { + properties.push( + { + name: 'who', + message: 'Enter author/s name/s', + required: true + }); + } + + if (!descriptor.linkGit) { + properties.push( + { + name: 'linkGit', + message: 'Enter link to repository', + required: true + }); + } + + prompt.get(properties, (err, result) => { + if (err) { + reject(err); + } + + descriptor.what = result.what; + descriptor.face = result.face; + if (result.who) { + descriptor.who = result.who; + } + if (result.linkGit) { + descriptor.linkGit = result.linkGit; + } + + // create descriptor json with values + wstream = fs.createWriteStream(descriptorJsonPath); + wstream.write(JSON.stringify(descriptor)); + wstream.close(); + + resolve(); + }); + }); + }); + } + + + function announceFinishedProcess() { + console.log('\n'); + process.stdout.write('Finished compiling addon\n'); + console.log(''); + process.stdout.write(colors.green('Addon can be found at ' )); + process.stdout.write(colors.cyan(path.resolve('./addons/' + npmId))); + process.stdout.write(colors.green('.\nIn order to publish to NPM: Navigate to the addon folder. Review the \'package.json\' file. Then run \'npm publish\'.\n')); + process.stdout.write(colors.green('A basic descriptor for your addon was created in the file \'descriptor.json\'. Please review it and edit fields accordingly.\n')); + process.stdout.write(colors.green('When you are ready to publish to Primo-Studio, create a pull request at ')); + process.stdout.write(colors.cyan('https://github.com/primousers/primostudio/tree/submit_here')); + process.stdout.write(colors.green(' appending your descriptor to the \'features.json\' file.\n')); + } + + + function handleError(err) { + // if fails at any time - clean addon folder + if (directoryName) { + fs.unlink(directoryName); + } + console.error(err.message); + } +})); \ No newline at end of file diff --git a/gulp/tasks/buildJsFromPrimoNodeModules.js b/gulp/tasks/07-primo-node-modules.js similarity index 82% rename from gulp/tasks/buildJsFromPrimoNodeModules.js rename to gulp/tasks/07-primo-node-modules.js index a56f65810..2f1fea420 100644 --- a/gulp/tasks/buildJsFromPrimoNodeModules.js +++ b/gulp/tasks/07-primo-node-modules.js @@ -5,7 +5,7 @@ let del = require("del"); let execSync = require('child_process').execSync; let gulp = require("gulp"); let gutil = require("gulp-util"); -let runSequence = require("run-sequence"); +let runSequence = require("gulp4-run-sequence"); /** * Metatask that executes the two atomic tasks in order @@ -17,16 +17,18 @@ let runSequence = require("run-sequence"); * * e.g. gulp run --view [ViewName] --reinstallNodeModules */ -gulp.task("reinstall-primo-node-modules", function() { +gulp.task("reinstall-primo-node-modules", gulp.series('select-view', function(cb) { if (config.getReinstallNodeModules()) { - runSequence(["delete-primo-node-modules", "install-primo-node-modules"]); + runSequence("delete-primo-node-modules", "install-primo-node-modules", cb); + return; } -}); + cb(); +})); /** * Deletes all primo-explore related node modules of the view package. */ -gulp.task("delete-primo-node-modules", function() { +gulp.task("delete-primo-node-modules", function(cb) { gutil.log("Starting deletion of the view package's primo explore related node modules."); del.sync([ @@ -34,6 +36,7 @@ gulp.task("delete-primo-node-modules", function() { ]); gutil.log("Finished deletion of the view package's primo explore related node modules."); + cb(); }); /** @@ -43,7 +46,7 @@ gulp.task("delete-primo-node-modules", function() { * This requires that all relevant primo-explore modules need to be referenced * in the package.json file in the root folder of the view package. */ -gulp.task("install-primo-node-modules", function() { +gulp.task("install-primo-node-modules", function(cb) { gutil.log("Starting re-installation of the view package's node modules using >npm install< command."); execSync('npm install', { @@ -63,4 +66,5 @@ gulp.task("install-primo-node-modules", function() { }); gutil.log("Finished re-installation of the view package's node modules using >npm install< command."); + cb(); }); diff --git a/gulp/tasks/servers.js b/gulp/tasks/08-servers.js similarity index 87% rename from gulp/tasks/servers.js rename to gulp/tasks/08-servers.js index ad519a3df..3e2574f0e 100644 --- a/gulp/tasks/servers.js +++ b/gulp/tasks/08-servers.js @@ -1,122 +1,126 @@ -'use strict'; - -let gulp = require('gulp'); -let zip = require('gulp-zip'); -let config = require('../config'); -let http = require('http'); -let https = require('https'); -let util = require('util'); -let browserSyncManager = require('../browserSyncManager'); -let primoProxy = require('../primoProxy'); -let glob = require('glob'); -let prompt = require('prompt'); -let runSequence = require('run-sequence'); - - - -gulp.task('setup_watchers', ['watch-js', 'watch-custom-scss', 'watch-css'], () => { - gulp.watch(config.buildParams.customPath(),() => { - return browserSyncManager.reloadServer(); - }); - gulp.watch(config.buildParams.customCssPath(),() => { - return gulp.src(config.buildParams.customCssPath()) - .pipe(browserSyncManager.streamToServer()); - }); -}); - - - -gulp.task('connect:primo_explore', function() { - let appName = 'primo-explore'; - browserSyncManager.startServer({ - label: 'production', - middleware:[ - function(req,res,next) { - let confPath = config.getVe() ? '/primaws/rest/pub/configuration' : '/primo_library/libweb/webservices/rest/v1/configuration'; - let confAsJsPath = '/primo-explore/config_'; - - let fixConfiguration = function(res,res1,isConfByFile){ - - let body = ''; - - res1.setEncoding('utf8'); - - res1.on("data", function(chunk) { - body = body + chunk; - }); - - res1.on("end", function(){ - let vid = config.view() || ''; - let customizationProxy = primoProxy.getCustimazationObject(vid,appName); - - if(isConfByFile){ - res.end(''); - - }else{ - let jsonBody = JSON.parse(body); - let newBodyObject = jsonBody; - - newBodyObject.customization = customizationProxy; - let newBody = JSON.stringify(newBodyObject); - - res.body = newBody; - - /*console.log('newBody: ' +newBody);*/ - res.end(newBody); - } - - - }); - } - - if(req.url.startsWith(confAsJsPath) || req.url.startsWith(confPath)) { - let isConfByFile = false; - if(req.url.startsWith(confAsJsPath)){ - isConfByFile = true; - } - - let url = config.PROXY_SERVER+req.url; - let base = config.PROXY_SERVER.replace('http:\/\/','').replace('https:\/\/',''); - let method = config.PROXY_SERVER.split('://')[0]; - let parts = base.split(':'); - let hostname = parts[0]; - let port = parts[1]; - - - let options = { - hostname: hostname, - port: port, - path: req.url, - method: 'GET', - headers: { - 'X-From-ExL-API-Gateway' : '1' - } - }; - let requestObject = http; - if(method === 'https') { - requestObject = https; - } - let req2 = requestObject.request(options, (res1) => { - fixConfiguration(res, res1,isConfByFile); - }); - req2.on('error', (e) => { - next(); - }); - - req2.write(''); - req2.end(); - - } - else { - next(); - } - - }, - primoProxy.proxy_function()], - port: 8003, - baseDir: appName - }); -}); - - -gulp.task('run', ['connect:primo_explore','reinstall-primo-node-modules','setup_watchers','custom-js','custom-scss','custom-css']); //watch +'use strict'; + +let gulp = require('gulp'); +let zip = require('gulp-zip'); +let config = require('../config'); +let http = require('http'); +let https = require('https'); +let util = require('util'); +let browserSyncManager = require('../browserSyncManager'); +let primoProxy = require('../primoProxy'); +let glob = require('glob'); +let prompt = require('prompt'); +let runSequence = require('gulp4-run-sequence'); + + + +gulp.task('setup_watchers', gulp.series('select-view', 'watch-js', 'watch-custom-scss', 'watch-css', (cb) => { + gulp.watch(config.buildParams.customPath(),(cb) => { + cb(); + return browserSyncManager.reloadServer(); + }); + gulp.watch(config.buildParams.customCssPath(),(cb) => { + cb(); + return gulp.src(config.buildParams.customCssPath()) + .pipe(browserSyncManager.streamToServer()); + }); + cb(); +})); + + + +gulp.task('connect:primo_explore', gulp.series('select-view', function(cb) { + let appName = 'primo-explore'; + browserSyncManager.startServer({ + label: 'production', + middleware:[ + function(req,res,next) { + let confPath = config.getVe() ? '/primaws/rest/pub/configuration' : '/primo_library/libweb/webservices/rest/v1/configuration'; + let confAsJsPath = '/primo-explore/config_'; + + let fixConfiguration = function(res,res1,isConfByFile){ + + let body = ''; + + res1.setEncoding('utf8'); + + res1.on("data", function(chunk) { + body = body + chunk; + }); + + res1.on("end", function(){ + let vid = config.view() || ''; + let customizationProxy = primoProxy.getCustimazationObject(vid,appName); + + if(isConfByFile){ + res.end(''); + + }else{ + let jsonBody = JSON.parse(body); + let newBodyObject = jsonBody; + + newBodyObject.customization = customizationProxy; + let newBody = JSON.stringify(newBodyObject); + + res.body = newBody; + + /*console.log('newBody: ' +newBody);*/ + res.end(newBody); + } + + + }); + } + + if(req.url.startsWith(confAsJsPath) || req.url.startsWith(confPath)) { + let isConfByFile = false; + if(req.url.startsWith(confAsJsPath)){ + isConfByFile = true; + } + + let url = config.PROXY_SERVER+req.url; + let base = config.PROXY_SERVER.replace('http:\/\/','').replace('https:\/\/',''); + let method = config.PROXY_SERVER.split('://')[0]; + let parts = base.split(':'); + let hostname = parts[0]; + let port = parts[1]; + + + let options = { + hostname: hostname, + port: port, + path: req.url, + method: 'GET', + headers: { + 'X-From-ExL-API-Gateway' : '1' + } + }; + let requestObject = http; + if(method === 'https') { + requestObject = https; + } + let req2 = requestObject.request(options, (res1) => { + fixConfiguration(res, res1,isConfByFile); + }); + req2.on('error', (e) => { + next(); + }); + + req2.write(''); + req2.end(); + + } + else { + next(); + } + + }, + primoProxy.proxy_function()], + port: 8003, + baseDir: appName + }); + cb(); +})); + + +gulp.task('run', gulp.series('select-view', 'connect:primo_explore','reinstall-primo-node-modules','setup_watchers','custom-js','custom-scss','custom-css')); //watch diff --git a/gulp/tasks/images.js b/gulp/tasks/09-images.js similarity index 84% rename from gulp/tasks/images.js rename to gulp/tasks/09-images.js index 3432a8d29..33059521f 100644 --- a/gulp/tasks/images.js +++ b/gulp/tasks/09-images.js @@ -7,7 +7,7 @@ const config = require('../config.js'); let buildParams = config.buildParams; gulp.task('watch-img', () => { - gulp.watch([buildParams.viewImgDir(), '!'+buildParams.customNpmImgPath()],['custom-img']); + gulp.watch([buildParams.viewImgDir(), '!'+buildParams.customNpmImgPath()], {interval: 1000, usePolling: true}, gulp.series('custom-img')); }); gulp.task('custom-img', () => { diff --git a/gulp/tasks/10-create-package.js b/gulp/tasks/10-create-package.js new file mode 100644 index 000000000..0771f44f5 --- /dev/null +++ b/gulp/tasks/10-create-package.js @@ -0,0 +1,19 @@ +'use strict'; +const gulp = require('gulp'); +const glob = require('glob'); +const prompt = require('prompt'); +const zip = require('gulp-zip'); +const config = require('../config.js'); + +gulp.task('create-package', gulp.series('select-view', 'custom-js','custom-scss','custom-css', function (cb) { + const code = config.view(); + console.log('Creating package for : ('+code+'.zip)'); + console.log(code); + console.log(' in : /packages'); + console.log('\r\n'); + console.log('............................................................................................................................................'); + return gulp.src(['./primo-explore/custom/'+code,'./primo-explore/custom/'+code+'/html/**','./primo-explore/custom/'+code+'/img/**','./primo-explore/custom/'+code+'/css/custom1.css','./primo-explore/custom/'+code+'/js/custom.js'], {base: './primo-explore/custom'}) + .pipe(zip(code+'.zip')) + .pipe(gulp.dest('./packages/')); + cb(); +})); diff --git a/gulp/tasks/buildCustomJs.js b/gulp/tasks/buildCustomJs.js deleted file mode 100644 index b4d5f8cd9..000000000 --- a/gulp/tasks/buildCustomJs.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -const gulp = require('gulp'); -const babel = require('gulp-babel'); -const config = require('../config.js'); -const rename = require("gulp-rename"); -const concat = require("gulp-concat"); -const debug = require('gulp-debug'); -const wrap = require("gulp-wrap"); -const glob = require('glob'); -const gutil = require('gulp-util'); -const fs = require("fs"); -const browserify = require("browserify"); - -let buildParams = config.buildParams; - -gulp.task('watch-js', () => { - gulp.watch([`${buildParams.viewJsDir()}/**/*.js`,'!'+buildParams.customPath()],['custom-js']); -}); - - -gulp.task('custom-js', ['custom-html-templates'],() => { - if(config.getBrowserify()) { - buildByBrowserify(); - } - else { - buildByConcatination(); - } - -}); - -function buildByConcatination() { - return gulp.src([buildParams.customModulePath(),buildParams.mainPath(),buildParams.customNpmJsPath(),'!'+buildParams.customPath(),'!'+buildParams.customNpmJsModulePath(),'!'+buildParams.customNpmJsCustomPath()]) - .pipe(concat(buildParams.customFile)) - .pipe(babel({ - presets: ['es2015'] - })) - .on("error", function(err) { - if (err && err.codeFrame) { - gutil.log( - gutil.colors.red("Browserify error: "), - gutil.colors.cyan(err.filename) + ` [${err.loc.line},${err.loc.column}]`, - "\r\n" + err.message + "\r\n" + err.codeFrame); - } - else { - gutil.log(err); - } - this.emit("end"); - }) - .pipe(wrap('(function(){\n"use strict";\n<%= contents %>\n})();')) - .pipe(gulp.dest(buildParams.viewJsDir())); -} - -function buildByBrowserify() { - return browserify({ - debug: true, - entries: buildParams.mainJsPath(), - paths:[ - buildParams.viewJsDir()+'/node_modules' - ] - }) - .transform("babelify",{presets: ["es2015"], plugins: ["transform-html-import-to-string"]}) - .bundle() - .pipe(fs.createWriteStream(buildParams.customPath())); -} diff --git a/gulp/tasks/createPackage.js b/gulp/tasks/createPackage.js deleted file mode 100644 index 2bf0d615d..000000000 --- a/gulp/tasks/createPackage.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; -var gulp = require('gulp'); -let glob = require('glob'); -let prompt = require('prompt'); -let zip = require('gulp-zip'); -let config = require('../config.js'); - -let buildParams = config.buildParams; - -gulp.task('create-package', function () { - var basedir = 'primo-explore/custom/'; - var customFolderExp = basedir+'*/'; - console.log('Please Choose a package to create:'); - glob(customFolderExp, {}, function (er, files) { - // Note elision, there is no member at 2 so it isn't visited - console.log('\r\n'); - files.forEach(function(element, index, array){ - console.log(index+1 + ': '+ element.replace(basedir,'').replace('/','')); - console.log('\r\n'); - }); - prompt.start(); - var property = { - name: 'package', - message: 'Please Choose the level you want to create the package for' - }; - prompt.get(property, function (err, result) { - - console.log('\r\n'); - var code = result.package; - - if(files[result.package - 1]){ - code = files[result.package - 1].replace(basedir,'').replace('/',''); - } - console.log('Creating package for : ('+code+'.zip)'); - console.log(code); - console.log(' in : /packages'); - console.log('\r\n'); - console.log('............................................................................................................................................'); - return gulp.src(['./primo-explore/custom/'+code,'./primo-explore/custom/'+code+'/html/**','./primo-explore/custom/'+code+'/img/**','./primo-explore/custom/'+code+'/css/custom1.css','./primo-explore/custom/'+code+'/js/custom.js'], {base: './primo-explore/custom'}) - .pipe(zip(code+'.zip')) - .pipe(gulp.dest('./packages/')); - }); - - }) - -}); diff --git a/gulpfile.js b/gulpfile.js index f98e15625..f264be801 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -8,8 +8,12 @@ const config = require('./gulp/config'); var options = minimist(process.argv.slice(2)); config.setView(options.view); -config.setVe(options.ve ? true : false); +config.setVe(!!options.ve); if (options.reinstallNodeModules) config.setReinstallNodeModules(options.reinstallNodeModules); if (options.proxy) config.setProxy(options.proxy); if (options.useScss) config.setUseScss(options.useScss); config.setBrowserify(options.browserify); +config.setSaml(options.saml); +config.setCas(options.cas); + +process.env.NODE_ENV = process.env.NODE_ENV || options.environment || 'production'; \ No newline at end of file diff --git a/package.json b/package.json index 8c1c193f6..9c2810b54 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,75 @@ { "name": "primo-explore-devenv", - "version": "1.0.5", - "description": "", - "author": "", + "version": "1.1.0", + "description": "The Exlibris Primo Open Discovery Framework", + "author": "noamamit92", "devDependencies": { + "babel-core": "6.26.3", + "babel-plugin-angularjs-annotate": "0.8.2", + "babel-plugin-transform-define": "^1.3.1", "babel-plugin-transform-html-import-to-string": "0.0.1", - "babel-preset-es2015": "6.6.0", - "babelify": "7.3.0", - "bluebird": "3.3.1", + "babel-preset-env": "1.7.0", + "babel-preset-es2015": "^6.24.1", + "babel-preset-stage-2": "^6.24.1", + "babelify": "8.0.0", + "bluebird": "3.5.4", "browser-sync": "2.9.9", - "browserify": "13.1.1", - "concat-stream": "1.5.1", - "connect-modrewrite": "0.8.2", - "css": "2.2.1", - "css-color-extractor": "0.0.4", - "css-color-extractor-cli": "0.0.1", - "del": "2.2.1", - "glob": "6.0.4", - "gulp": "3.9.1", - "gulp-angular-templatecache": "^2.0.0", - "gulp-autoprefixer": "3.1.0", - "gulp-babel": "6.1.2", - "gulp-clone": "1.0.0", - "gulp-concat": "2.6.0", - "gulp-cssnano": "2.1.2", - "gulp-debug": "2.1.2", - "gulp-flatten": "^0.3.1", - "gulp-plumber": "1.1.0", - "gulp-rename": "1.2.2", - "gulp-sass": "2.3.2", - "gulp-sourcemaps": "1.6.0", - "gulp-template": "4.0.0", - "gulp-wrap": "0.11.0", - "gulp-zip": "3.1.0", - "http-response-object": "1.1.0", - "listdirs": "3.0.0", - "lodash": "4.16.4", - "merge-stream": "1.0.0", - "minimist": "1.2.0", + "browserify": "16.2.3", + "camel-case": "^3.0.0", + "colors": "^1.3.3", + "concat-stream": "^2.0.0", + "connect-modrewrite": "0.10.2", + "css": "^2.2.4", + "css-color-extractor": "0.0.5", + "css-color-extractor-cli": "0.0.2", + "del": "4.1.1", + "fs-extra": "7.0.1", + "glob": "7.1.3", + "graceful-fs": "4.1.15", + "gulp": "4.0.2", + "gulp-angular-templatecache": "^2.2.7", + "gulp-autoprefixer": "6.1.0", + "gulp-babel": "7.0.1", + "gulp-clone": "2.0.1", + "gulp-concat": "2.6.1", + "gulp-cssnano": "^2.1.3", + "gulp-debug": "4.0.0", + "gulp-flatten": "^0.4.0", + "gulp-plumber": "1.2.0", + "gulp-rename": "1.4.0", + "gulp-sass": "^5.1.0", + "gulp-sourcemaps": "2.6.5", + "gulp-streamify": "1.0.2", + "gulp-template": "5.0.0", + "gulp-uglify": "3.0.2", + "gulp-util": "3.0.8", + "gulp-wrap": "^0.15.0", + "gulp-zip": "4.2.0", + "gulp4-run-sequence": "^1.0.1", + "http-response-object": "3.0.2", + "listdirs": "3.1.1", + "lodash": "4.17.19", + "merge-stream": "1.0.1", + "minimatch": "3.0.4", + "minimist": "1.2.5", "prompt": "1.0.0", - "request": "2.72.0", - "require-dir": "0.3.0", - "run-sequence": "1.2.1", - "shelljs": "0.6.0", - "tar-fs": "1.13.0", - "protractor": "5.0.0", - "fs-extra": "2.1.2", - "protractor-image-comparison": "1.2.3" + "protractor": "5.4.2", + "protractor-image-comparison": "3.1.0", + "request": "^2.88.0", + "require-dir": "1.2.0", + "run-sequence": "^2.2.1", + "shelljs": "0.8.3", + "stream-to-promise": "^2.2.0", + "tar-fs": "2.0.0", + "vinyl-buffer": "1.0.1", + "vinyl-source-stream": "2.0.0" }, "engines": { - "node": ">=4.2" + "node": ">=16.17.0" }, - "name": "primo-explore-devenv", - "version": "1.0.1", - "description": "The Exlibris Primo Open Discovery Framework", - "author": "noamamit92", + "browserslist": [ + "last 2 version", + "> 2%" + ], "repository": "https://github.com/ExLibrisGroup/primo-explore-devenv" - } diff --git a/primo-explore/custom/.gitignore b/primo-explore/custom/.gitignore index 86d0cb272..f863544db 100644 --- a/primo-explore/custom/.gitignore +++ b/primo-explore/custom/.gitignore @@ -1,4 +1 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file +# Add entries to prefixed with '!' to unhide your view files \ No newline at end of file diff --git a/primo-explore/tmp/.gitignore b/primo-explore/tmp/.gitignore index 86d0cb272..f863544db 100644 --- a/primo-explore/tmp/.gitignore +++ b/primo-explore/tmp/.gitignore @@ -1,4 +1 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file +# Add entries to prefixed with '!' to unhide your view files \ No newline at end of file diff --git a/tests/manual/TESTBROWSERIFY/README.md b/tests/manual/TESTBROWSERIFY/README.md new file mode 100644 index 000000000..865ec76b3 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/README.md @@ -0,0 +1,48 @@ + +# The Primo New UI Customization Workflow Development Environment + + +## Package documentation + +The development package allows you to configure : + +- css + +- images + +- html + +- JavaScript + +- The root directory of the package should be named either by the `viewCode` or `CENTRAL_PACKAGE` in case of a consortia level package +- Whether you develop a consortia level package or a view level package the process remains the same +- Once deployed the hierarchy is as follows: + 1. For css - use the cascading ability of css and load the consortia level (CENTRAL_PACKAGE) css first and the view level css afterwards + 2. For images and html - the system checks for every file if it exists in each level - and prefers the view level file if exists + 3. For JavaScript - the two package types define 2 different Angular modules: + - ```var app = angular.module('viewCustom', ['angularLoad']);``` + - ```var app = angular.module('centralCustom', ['angularLoad']);``` + + and loads both of the modules, + +- For each configuration type there is a specified folder in the custom package folder (that can be downloaded form your Primo Back Office) +- In each folder you will find a specific README.md file with recipes/examples. + + [CSS](/VIEW_CODE/css/README.md "css documentation") + + [HTML](/VIEW_CODE/html/README.md "html documentation") + + [Images](/VIEW_CODE/img/README.md "images documentation") + + [JavaScript](/VIEW_CODE/js/README.md "javascript documentation") + +- For `colors.json.txt` instructions - please see [CSS](/VIEW_CODE/css/README.md "css documentation") documentation + + + + + + + + + diff --git a/tests/manual/TESTBROWSERIFY/colors.json b/tests/manual/TESTBROWSERIFY/colors.json new file mode 100644 index 000000000..963f80de6 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/colors.json @@ -0,0 +1,15 @@ +{ + "primary": "red", + "secondary" : "green", + "backgroundColor" : "yellow", + "links": "#3D6E94", + "warning": "tomato", + "positive": "#0f7d00", + "negative": "gray", + "notice": "#B84D00", + "linkTitle": "#33FFFF", + "citation": "tomato", + "citationTitles": "rgb(68, 112, 123)", + "personalization": "#7d1538" +} + diff --git a/tests/manual/TESTBROWSERIFY/css/README.md b/tests/manual/TESTBROWSERIFY/css/README.md new file mode 100644 index 000000000..5df326507 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/css/README.md @@ -0,0 +1,168 @@ +# The Primo New UI Customization Workflow Development Environment + + +## css documentation + +- Primo uses Angular Directives massively in this project + +- To learn more about directives see: +> https://docs.angularjs.org/guide/directive + +- Primo uses external directives from the Angular-material framework : +> https://material.angularjs.org/latest/ + +- Those directives are tagged by a prefix : "md-" + +- Primo also creates its own directives which are tagged by the "prm-" prefix. + + +Example: +``` +
+ + + + + + + + + +
+``` + + +- You can see in the example how we use : + +1. An HTML5 tag - header +2. A Primo directive : prm-topbar , prm-search-bar. +3. An external material design directive : md-progress-bar : +> https://material.angularjs.org/latest/api/directive/mdProgressLinear + + + +- When defining css rules it is important to understand the css cascading/specifity logic: + +> http://www.w3.org/TR/css3-cascade/ + +> https://specificity.keegan.st/ + + + + +- When you start working on customizing your css be aware of the ability to define css selectors based on the directive name, which is actually equivalent +to an html tag - this will enable you changing the design of a component cross-system without relying on id's/classes + +- For the example above we can define selectors: + +``` +prm-topbar input {....} +prm-topbar.md-primoExplore-theme input {....} +``` +- Primo is using a theme inside angular-material to define a palette of colors see: +> https://material.angularjs.org/latest/Theming/01_introduction + + +- This means that you will often encounter a class "md-primoExplore-theme" attached to elements. + + + +## Recipes/Examples: + + +# css Recipe 1 - Color Scheme (Starting from August 2016 Release) + +- Open the `colors.json.txt` file in the root of your view folder +- You will see a json object with our default color scheme: + + ``` + { + "primary": "#53738C", + "secondary" : "#A9CDD6", + "backgroundColor" : "white", + "links": "#5C92BD", + "warning": "tomato", + "positive": "#0f7d00", + "negative": "gray", + "notice": "#e08303" + } + ``` + +- Since November 2016 release - we are giving you the ability to easily customize the majority of the following +colors - primary, secondary, backgroundColor, links, warning, positive, negative, notice - just change the definition and save the file. + +The colors are mapped to different elements in the user interface: + +![Color Changes image](../../help_files/colors3.png "Color Changes") + +- Open a new command line window + +- cd to the project base directory (C:\**\**\primo-explore-devenv) +- Run `gulp app-css --view ` for example: + `gulp app-css --view Auto1` +- for Primo Ve customers add the --ve flag at the end of the command for example: + `gulp app-css --view Auto1 --ve` +- A new file will be created on your package css directory named: `app-colors.css` +- This file will contain all of the primo-explore theme color definitions. + We will continue to add more color definitions to extend this ability +- Refresh your browser to see the changes take affect +- For example, for the following `colors.json.txt` file: + +``` +{ + "primary": "#512DA8", + "secondary" : "#D1C4E9", + "backgroundColor" : "#BDBDBD", + "links": "#009688", + "warning": "#FF5722" +} + + +``` + +You will get: + + ![Color Changes image](../../help_files/colors1.png "Color Changes") + + ![Color Changes image](../../help_files/colors2.png "Color Changes") + +# css Recipe 2 - Moving the Facets to the Left + + +- Select the parent container containing the search result and the facets +- Copy the selector definition using your browsers' dev tools +- Define the container as + +``` +display:flex; +flex-flow:row-reverse; +``` + + +- complete css definition: +``` +prm-search > md-content.md-primoExplore-theme .main, prm-search > md-content.md-primoExplore-theme.main { + display: -webkit-flex; !* Safari *! + -webkit-flex-flow: row-reverse wrap; !* Safari 6.1+ *! + display: flex; + flex-flow: row-reverse wrap; + +} +.screen-gt-sm .sidebar{ + webkit-flex: 0 0 15%; + flex: 0 0 15%; +} +``` +- Save and refresh your browser + +- The result: + + + ![Facets image](../../help_files/facets.png "Factes Changes") + + + + + + + diff --git a/tests/manual/TESTBROWSERIFY/css/app-colors.css b/tests/manual/TESTBROWSERIFY/css/app-colors.css new file mode 100644 index 000000000..e69de29bb diff --git a/tests/manual/TESTBROWSERIFY/css/custom1.css b/tests/manual/TESTBROWSERIFY/css/custom1.css new file mode 100644 index 000000000..e69de29bb diff --git a/tests/manual/TESTBROWSERIFY/html/README.md b/tests/manual/TESTBROWSERIFY/html/README.md new file mode 100644 index 000000000..3f8d98b15 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/html/README.md @@ -0,0 +1,152 @@ +# The Primo New UI Customization Workflow Development Environment + + +## html documentation + + - In this folder you will find static html files in their OTB state + - The files are separated into directories (starting from the November 2016 Release) + - You can edit the html to comply with your library requirements + - To support multiple languages in your interface just add a suffix with the language code to your file-name, + For example: + + 1. homepage_fe_FR.html (in the August 2016 Release use home_fe_FR.html) + 2. help_de_DE.html(Available in the November 2016 Release) + + - This is how your directory structure should look like: + + ![August 2016 Release structure image](../../help_files/htmlStructureAug.png "August 2016 Release structure") + + + - Note that you can use Angular Material directives in your html: + > https://material.angularjs.org/latest/ + + +## Email templates + Starting from 2020 Primo supports a new html template to customize email messages sent to patrons'. + + To customize this template create a file named email_fr_FR.html (based on your language suffix) and upload it using the html directory in your customization package. + + In this html file you can design your own email template using html. + + Since Primo supports the email action on multiple records you should add the following attribute to the html element represnting a record: + + ```ng-if="$ctrl.parentCtrl.fullViewLoaded" ng-repeat="item in $ctrl.parentCtrl.delayedItems"``` + + for example: + + For an email template layout as below the attribute should be added to the records container: + + ``` +
+ + +
+ + + + + + ... + +
+
+
+ ``` + + When editing the template you can: + + 1. Use regular html (use best practices for email templating: https://mailchimp.com/help/about-html-email/) to design the layout of your email based on your preferences. + + 2. You can take advantage of some of our OTB directives in your templates: +``` + + + + + + + + + ``` + 3. You can reference the sent item(pnx/record) using angular syntax to present the relevant data: + + use the curly brackets to access the pnx diectly - for example: +``` + {{item.pnx.display.title}} +``` + or loop over values using the ng-repeat directive to add multiple fields: + +```
+
Additional Information From the Record:
+ OCLCID: +
{{oclcid}}
+``` +``` ``` + + + + + + +**Notice :** Any link in your template will be removed - for security reasons, we will not allow links other than the link to the record to avoid email exploits. + +To add the link to the full record you can either: + 1. Use the + + ``` + + + ``` +directive in your template - This includes the link to the record + +2. Use the following code snippet in your template: + + ``` + + {{item.pnx.display.title[0]}} + + ``` + +To use your library logo (not the OTB ) you can use your own html, with an img element that has a class attribute of *logo-image* immediatly following the opening tag, for example: + + ``` + + + + + + +
+ +
+``` + + + ## Examples + + Full examples of email templates can be found in the help files folder folder: + + 1. [The Out of the Box template for emails](../../help_files/email_en_US.html) email_en_US.html + + 2. [A template based on the OTB - brief + custom fields + availability](../../help_files/email_en_US-brief+additionalField+availability.html) + will produce the following email: + + ![example brief](../../help_files/example-bried-and-subject.png "example brief") + + 3. [A template based on an open source email template with no Primo directives - just use of the ```{{item.pnx.display.title}}```](../../help_files/email_en_US_Details.html) + will produce the following email: + + ![example external](../../help_files/example-external.png "example external") + + + + diff --git a/tests/manual/TESTBROWSERIFY/html/home_en_US.html b/tests/manual/TESTBROWSERIFY/html/home_en_US.html new file mode 100644 index 000000000..96b69aa8e --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/html/home_en_US.html @@ -0,0 +1 @@ +Customized diff --git a/tests/manual/TESTBROWSERIFY/img/README.md b/tests/manual/TESTBROWSERIFY/img/README.md new file mode 100644 index 000000000..eb4c0bb82 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/img/README.md @@ -0,0 +1,20 @@ +# The Primo New UI Customization Workflow Development Environment + + +## images documentation + + - Primo allows the customization of the following images; + 1. Library Logo - just place a file named `library-logo.png` in this location + 2. Library favicon - just place a file named `favicon.ico` in this location + 3. Default Resource Types - just place a files following this convention : + `icon_.png` + + For Example: + `icon_book.png` + + + + + + + diff --git a/tests/manual/TESTBROWSERIFY/img/icon_book.png b/tests/manual/TESTBROWSERIFY/img/icon_book.png new file mode 100644 index 000000000..048bdfe88 Binary files /dev/null and b/tests/manual/TESTBROWSERIFY/img/icon_book.png differ diff --git a/tests/manual/TESTBROWSERIFY/img/library-logo.png b/tests/manual/TESTBROWSERIFY/img/library-logo.png new file mode 100644 index 000000000..4889cb614 Binary files /dev/null and b/tests/manual/TESTBROWSERIFY/img/library-logo.png differ diff --git a/tests/manual/TESTBROWSERIFY/js/README.md b/tests/manual/TESTBROWSERIFY/js/README.md new file mode 100644 index 000000000..cfc360d7a --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/js/README.md @@ -0,0 +1,275 @@ +# The Primo New UI Customization Workflow Development Environment + + +## JavaScript documentation + +- When you want to add functionality to your Primo installation you will be using Angular Directives. + +- To learn more about directives see: +> https://docs.angularjs.org/guide/directive + +- Primo uses external directives from the Angular-material framework: +> https://material.angularjs.org/latest/ + +- Those directives are tagged by a prefix : "md-" + +- Primo also creates its own directives which are tagged by the "prm-" prefix. + + +Example: +``` +
+ + + + + + + + + +
+``` + + +- You can see in the example how we use : + +1. An HTML5 tag - header +2. A Primo directive : prm-topbar , prm-search-bar. +3. An external material design directive : md-progress-bar : +> https://material.angularjs.org/latest/api/directive/mdProgressLinear + + + +## Concept + +- When You want to add your own JavaScript functionality - you will need to plug-in to placeholder Directives we added to the system, by creating your own placeholder Directive. +- Those directives are added as the last child element for every Primo directive (defined by the `prm-` prefix) +- The placeholder directives are injected (as input) with the Controller of their parent, thus have access to the data model of the parent directive +- Use the examples below as starting points for your JavaScript plug-in directives +- Learn about Angular Directives to better understand the different abilities this workflow offers +> https://docs.angularjs.org/guide/directive + + + +## Recipes/Examples: + +# Note: + +The examples below use the back tic '`' for templates - which will work using babel (Documentation on how to do so will be shared) +This causes the examples to fail on IE11 browsers. + +To solve this you can replace the '`' with regular apostrophe ("'") and use a single line template (less readable but works just as well). + + + + +# JavaScript Recipe 1 - a Static `hello world` html Message + + +- Use the `showDirectives` (located in the root directory of this package is the showDirectives.txt file +, just add the content of the file as a bookmark to your browser) scriplet to identify the `prmSearchBarAfter` directive which you will plugin to + + +![Show Directives image](../../help_files/showDirectives.png "Show Directives Changes") + +- Edit the primo-explore/custom/js/custom.js file and add a component declaration for `myInstitutionComponent` and a component declaration +for the `prmSearchBarAfter` directive + + ``` + app.component('myInstitutionComponent', { + + + }); + + app.component('prmSearchBarAfter', { + + + }); + ``` +- Add the template property with your static message + + ``` + app.component('myInstitutionComponent', { + template: `Hello World` + }); + ``` +- Add the template property with your component template (myInstitutionComponent) + + ``` + app.component('prmSearchBarAfter', { + bindings: {parentCtrl: `<`}, + template: `` + }); + ``` + + +- Save and refresh your browser + +![Hello World image](../../help_files/js1.png "Hello World Changes") + +# JavaScript Recipe 2 - a Dynamic Directive +- Use the `showDirectives` scriplet to identify the `prmSearchBarAfter` directive which you will plugin to +- Run the following command in your browsers' console tab: +``` + angular.reloadWithDebugInfo() +``` +- Focus on the `prmSearchBarAfter` directive + +![Focus image](../../help_files/js2.png "Focus") + +- Run the following command in your browsers' console tab: +``` + angular.element($0).scope().$ctrl (angular.element($0).scope().ctrl in version earlier than February 2017) +``` + +- Review the properties of the directive to decide which data elements can be used, avoid methods/functions as they wont be backwards compatible + +![properties image](../../help_files/js3.png "properties") + + +- Edit primo-explore/custom/js/custom.js file and add: a component declaration for the `prmSearchBarAfter` directive, and a component declaration for the `myInstitutionComponent` + +- Add a binding definition the input parentCtrl for both components +``` + bindings: {parentCtrl: '<'}, +``` +- Add a controller definition for the `myInstitutionComponent` component: +``` + controller: 'MyInstitutionComponentController', +``` +- Define a controller with 2 getter methods to return the query and selected scope +``` +app.controller('MyInstitutionComponentController', [function () { + var vm = this; + + + vm.getSelectdScope = getSelectdScope; + vm.getQuery = getQuery; + + + function getSelectdScope() { + return vm.parentCtrl.scopeField; + } + + function getQuery() { + return vm.parentCtrl.mainSearchField; + } + }]); + +``` +- Edit the `prmSearchBarAfter` directive template to reference the `myInstitutionComponent` +``` +template: `` +``` +- Edit the `myInstitutionComponent` directive template to reference the getter methods +``` +template: `
+ + + + This is a demo presenting the ability to display query + information below the search box + Query: {{$ctrl.getQuery()}} + Scope: {{$ctrl.getSelectdScope()}} + + +
+
+
+ + Action 1 + Action 2 + +
+
` + + ``` +- Save and refresh your browser + +![dynamic example image](../../help_files/js4.png "dynamic example") + +# JavaScript Recipe 3 - Adding the Altmetrics Widget +- Use the `showDirectives` scriplet to identify the `prmFullViewAfter` directive which you will plugin to + +![Altmetrics example image](../../help_files/js5.png "Altmetrics example") + +- Run the following command in your browsers' console tab: +`angular.reloadWithDebugInfo()` +- Focus on the `prmFullViewAfter` directive +- Run the following command in your browsers' console tab: +`angular.element($0).scope().ctrl` + +- Review the properties of the directive to decide which data elements can be used, avoid methods/functions as they wont be backwards compatible + +![Altmetrics example 2 image](../../help_files/js6.png "Altmetrics 2 example") + +- Edit primo-explore/custom/js/custom.js file and add: a component declaration for the `prmFullViewAfter` directive, and a component declaration for the `myInstitutionComponent` + +- Add a binding definition the input parentCtrl for both components +``` + bindings: {parentCtrl: '<'}, +``` +- Add a controller definition for the `myInstitutionComponent` component: +``` + controller: 'MyInstitutionComponentController', +``` +- Define a controller that populates the doi and loads the Altmetrics js file +``` +app.controller('MyInstitutionComponentController', ['angularLoad', function (angularLoad) { + var vm = this; + vm.doi = vm.parentCtrl.item.pnx.addata.doi[0] || ''; + + vm.$onInit = function () { + angularLoad.loadScript('https://d1bxh8uas1mnw7.cloudfront.net/assets/embed.js?' + Date.now()).then(function () { + + }); + }; + }]); +``` +- Edit the `prmFullViewAfter` directive template to reference the `myInstitutionComponent` +``` +template: `` +``` +- Edit the `myInstitutionComponent` directive template to add the Altmetrics div and bind the data-doi attribute to the controller +``` +app.component('myInstitutionComponent', { + bindings: {parentCtrl: '<'}, + controller: 'MyInstitutionComponentController', + template: `
+
+
+

+ Social Popularity Statistics (AltMetrics) : +

+ + +
+
+
+
+
+
+
+
+
+
+
+
+
` + }); + ``` + +- Edit the custom1.css file and add the following definitions: + + ``` + .full-view-section.loc-altemtrics{ + background-color: #f3f3f3; + margin-top:0px; + padding-left: 3em; + } + ``` + +- Save and refresh your browser + +![Altmetrics example 3 image](../../help_files/js7.png "Altmetrics 3 example") diff --git a/tests/manual/TESTBROWSERIFY/js/custom.js b/tests/manual/TESTBROWSERIFY/js/custom.js new file mode 100644 index 000000000..4899da6e6 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/js/custom.js @@ -0,0 +1,2 @@ +!function a(i,c,d){function u(t,e){if(!c[t]){if(!i[t]){var r="function"==typeof require&&require;if(!e&&r)return r(t,!0);if(l)return l(t,!0);var n=new Error("Cannot find module '"+t+"'");throw n.code="MODULE_NOT_FOUND",n}var o=c[t]={exports:{}};i[t][0].call(o.exports,function(e){return u(i[t][1][e]||e)},o,o.exports,a,i,c,d)}return c[t].exports}for(var l="function"==typeof require&&require,e=0;e"})},{"./test.module.js":2}],2:[function(e,t,r){"use strict";angular.module("testModule",[]).factory("testModuleService",[function(){var e={manipulate:function(e){return e.split("").reverse().join("")}};return e}]).controller("testModuleController",["testModuleService",function(e){var t=this;t.getSelectdScope=function(){return e.manipulate(t.prmSearchBar.scopeField)},t.getQuery=function(){return t.prmSearchBar.mainSearchField}}]).component("testComponent",{require:{prmSearchBar:"^prmSearchBar"},controller:"testModuleController",template:'
\n \n \n \n This is a demo presenting the ability to display query\n information below the search box\n Query: {{$ctrl.getQuery()}}\n Scope: {{$ctrl.getSelectdScope()}}\n \n \n
\n
\n
\n \n Action 1\n Action 2\n \n
\n
'})},{}]},{},[1]); +//# sourceMappingURL=custom.js.map diff --git a/tests/manual/TESTBROWSERIFY/js/custom.js.map b/tests/manual/TESTBROWSERIFY/js/custom.js.map new file mode 100644 index 000000000..67ab98385 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/js/custom.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["node_modules/browser-pack/_prelude.js","primo-explore/custom/TESTBROWSERIFY/js/main.js","primo-explore/custom/TESTBROWSERIFY/js/test.module.js"],"names":["r","e","n","t","o","i","f","c","require","u","a","Error","code","p","exports","call","length","1","module","angular","component","template","factory","svc","manipulate","str","split","reverse","join","controller","testModuleService","vm","this","getSelectdScope","prmSearchBar","scopeField","getQuery","mainSearchField"],"mappings":"CAAA,SAAAA,EAAAC,EAAAC,EAAAC,GAAA,SAAAC,EAAAC,EAAAC,GAAA,IAAAJ,EAAAG,GAAA,CAAA,IAAAJ,EAAAI,GAAA,CAAA,IAAAE,EAAA,mBAAAC,SAAAA,QAAA,IAAAF,GAAAC,EAAA,OAAAA,EAAAF,GAAA,GAAA,GAAAI,EAAA,OAAAA,EAAAJ,GAAA,GAAA,IAAAK,EAAA,IAAAC,MAAA,uBAAAN,EAAA,KAAA,MAAAK,EAAAE,KAAA,mBAAAF,EAAA,IAAAG,EAAAX,EAAAG,GAAA,CAAAS,QAAA,IAAAb,EAAAI,GAAA,GAAAU,KAAAF,EAAAC,QAAA,SAAAd,GAAA,OAAAI,EAAAH,EAAAI,GAAA,GAAAL,IAAAA,IAAAa,EAAAA,EAAAC,QAAAd,EAAAC,EAAAC,EAAAC,GAAA,OAAAD,EAAAG,GAAAS,QAAA,IAAA,IAAAL,EAAA,mBAAAD,SAAAA,QAAAH,EAAA,EAAAA,EAAAF,EAAAa,OAAAX,IAAAD,EAAAD,EAAAE,IAAA,OAAAD,EAAA,CAAA,CAAAa,EAAA,CAAA,SAAAT,EAAAU,EAAAJ,gBCAAN,EAAQ,oBAERW,QACKD,OAAO,aAAc,CAAC,eACtBE,UAAU,oBAAqB,CAC5BC,SACI,uC,wDCNIF,QACKD,OAAO,aAAc,IACrBI,QAAQ,oBAAqB,CAC1B,WACI,IAAIC,EAAM,CACVC,WAAiB,SAAUC,GACvB,OAAOA,EAAIC,MAAM,IAAIC,UAAUC,KAAK,MAExC,OAAOL,KAGdM,WAAW,uBAAwB,CAChC,oBACA,SAAUC,GACN,IAAIC,EAAKC,KACTD,EAAGE,gBAAkB,WACjB,OAAOH,EAAkBN,WAAWO,EAAGG,aAAaC,aAGxDJ,EAAGK,SAAW,WACV,OAAOL,EAAGG,aAAaG,oBAIlCjB,UAAU,gBAAiB,CACxBZ,QAAS,CACL0B,aAAc,iBAElBL,WAAY,uBACZR,SAAA","file":"custom.js","sourcesContent":["(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i'\r\n });\r\n"," angular\r\n .module('testModule', [])\r\n .factory('testModuleService', [\r\n function () {\r\n var svc = {};\r\n svc.manipulate = function (str) {\r\n return str.split(\"\").reverse().join(\"\");\r\n };\r\n return svc;\r\n },\r\n ])\r\n .controller('testModuleController', [\r\n 'testModuleService',\r\n function (testModuleService) {\r\n var vm = this;\r\n vm.getSelectdScope = function() {\r\n return testModuleService.manipulate(vm.prmSearchBar.scopeField);\r\n }\r\n\r\n vm.getQuery = function() {\r\n return vm.prmSearchBar.mainSearchField;\r\n }\r\n },\r\n ])\r\n .component('testComponent', {\r\n require: {\r\n prmSearchBar: '^prmSearchBar',\r\n },\r\n controller: 'testModuleController',\r\n template:\r\n `
\r\n \r\n \r\n \r\n This is a demo presenting the ability to display query\r\n information below the search box\r\n Query: {{$ctrl.getQuery()}}\r\n Scope: {{$ctrl.getSelectdScope()}}\r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n Action 1\r\n Action 2\r\n \r\n
\r\n
`,\r\n });\r\n"]} \ No newline at end of file diff --git a/tests/manual/TESTBROWSERIFY/js/main.js b/tests/manual/TESTBROWSERIFY/js/main.js new file mode 100644 index 000000000..b4db15302 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/js/main.js @@ -0,0 +1,8 @@ +require('./test.module.js'); +//module.exports = 'testModule'; +angular + .module("viewCustom", ["testModule"]) + .component("prmSearchBarAfter", { + template: + '' + }); diff --git a/tests/manual/TESTBROWSERIFY/js/test.module.js b/tests/manual/TESTBROWSERIFY/js/test.module.js new file mode 100644 index 000000000..7e0867cfa --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/js/test.module.js @@ -0,0 +1,50 @@ + angular + .module('testModule', []) + .factory('testModuleService', [ + function () { + var svc = {}; + svc.manipulate = function (str) { + return str.split("").reverse().join(""); + }; + return svc; + }, + ]) + .controller('testModuleController', [ + 'testModuleService', + function (testModuleService) { + var vm = this; + vm.getSelectdScope = function() { + return testModuleService.manipulate(vm.prmSearchBar.scopeField); + } + + vm.getQuery = function() { + return vm.prmSearchBar.mainSearchField; + } + }, + ]) + .component('testComponent', { + require: { + prmSearchBar: '^prmSearchBar', + }, + controller: 'testModuleController', + template: + `
+ + + + This is a demo presenting the ability to display query + information below the search box + Query: {{$ctrl.getQuery()}} + Scope: {{$ctrl.getSelectdScope()}} + + +
+
+
+ + Action 1 + Action 2 + +
+
`, + }); diff --git a/tests/manual/TESTBROWSERIFY/js/test.service.js b/tests/manual/TESTBROWSERIFY/js/test.service.js new file mode 100644 index 000000000..3579f665b --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/js/test.service.js @@ -0,0 +1,11 @@ +app.service('TestService', [function () { + let vm = this; + + + vm.manipulate = manipulate; + + + function manipulate(str){ + return str.split("").reverse().join(""); + } +}]); diff --git a/tests/manual/TESTBROWSERIFY/scss/partials/_variables.scss b/tests/manual/TESTBROWSERIFY/scss/partials/_variables.scss new file mode 100644 index 000000000..c70d2883c --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/scss/partials/_variables.scss @@ -0,0 +1,225 @@ +//// VARIABLES +// The complex color calculation were created with http://razorjam.github.io/sasscolourfunctioncalculator/ + +// Fonts family +$serif: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif; + +// Font weights +$light: 300; +$normal: 400; +$bold: 600; +$heavy: 900; + +// Font size +$defaultFontSize: 15px; + +// Shapes +$radius: 3px; + +// Padding +$paddingBase: 1em; +$paddingExtra: 1.5em; +$paddingMedium: 1.3em; +$paddingLarge: 1em; +$paddingSmall: .5em; +$paddingTiny: .25em; + +// colors + +$muteOrange: red; + + + +$almostBlack: #777; + + + +$titles: rgb(68, 112, 123); +$frbr: #ed9100; +$pink: rgb(255, 171, 200); + +$dark: #3a3a3a; +$darker: darken(saturate(adjust-hue($dark, 0.0000), 0.0000), 6.6667); + +// lighter background color + + +$highlightYellow: #FFFCC4; //stronger +$highlight: darken(desaturate(adjust-hue($highlightYellow, -4.0920), 16.0000), 8.0392); +$highlightText: #ffe564; +$success: #3EA03E; +//$green: #3EA03E; // redundancy of 'success' +$green: #198A19; +//$orange: #CE8015; //#E0962E; +$muteOrange: #C59655; +$yellow: #F1C16F; +$overlayDarkDG: rgba(68, 68, 68, 0.6); + +// $availabiltyDetails: desaturate(lighten($midGrey,5%), 5%); +//$maybe: $orange; + +$alert: #F7EDA3; +$alert-hue1: darken(desaturate(adjust-hue($alert, 0.3074), 15.3043), 2.9412); + + +$disabled: #aaa; +$courseColor: #55909B; + +$primary: red; +$primary-hue1: darken($primary, 3%); +$primary-hue2: darken($primary, 5%); +$primary-hue3: darken($primary-hue2, 2%); +$primary-hue4: darken($primary, 10%); +$primary-hue5: darker($primary, -5%); + +$secondary: green; +$secondary-hue1: darken(saturate(adjust-hue($secondary, -6.5946), 9.1452), 26.2745); +$secondary-hue2: darken($secondary, 20%); +$secondary-hue3:darken(saturate(adjust-hue($secondary, -12), 30.28), 47.65); // "show more" buttons in facets +$secondary-hue4: darken(saturate(adjust-hue($secondary, -7), 64.57), 13.92); + + +$backgroundColor: yellow; + +$positive: #0f7d00; + +$negative: gray; + +$background: darken(saturate(adjust-hue($backgroundColor, 0.0000), 0.0000), 8.6275); //redundancy of almostwhite + +$background-hue1: darken($backgroundColor, 3%); // darker 1 (i.e: background for locations filter bar) +// $background-hue2: lighten(saturate(adjust-hue($backgroundColor, 0.0000), 0.0000), 3.9216); // lighter 1 (i.e: background for full view dialog) +$background-hue2: lighten($background, 4%); +$background-hue3: lighten(saturate(adjust-hue($backgroundColor, 0.0000), 0.0000), 5.8824); // lighter 2 (i.e: background for actions forms) + +$background-hue4: lighten($background, 3%); +$background-hue5: transparentize($background, .5); +$background-hue6: darken($background, 1%); +$background-hue7: darken($background, 10%); +$background-hue8: lighten($background, 6%); +$background-hue9: darken($background, 5%); +$background-hue10: darken($background, 12%); +$background-hue11: darken($background, 2%); +$background-hue12: darken($background, 7%); +$background-hue13: lighten($background, 2%); + +$white: white; + +$almostWhite: lighten(saturate(adjust-hue($background, 60.0000), 20.0000), 0.7843); +$bg: $background; +$nearlyWhite: darken(saturate(adjust-hue($backgroundColor, 0.0000), 0.0000), 5.4902); // stronger +$blueGrey: #ccc; +$midGrey: #53738C; //darken(saturate(adjust-hue($blueGrey, 206.3158), 25.5605), 36.2745); +$actionsBackground: lighten($background, 6%); +$autoCompleteBg: $backgroundColor; +$sectionHighlight: darken(desaturate($background, 0), 5%); +$snippets:#999; +$lightenGrey: #6d6d6d; + +$links: #3D6E94; + +$links-background-hue1: transparentize($links, 1); + +$links: $links; +$links-hue-1: darken(saturate($links, 5%), 5%); +$links-hue-5: darken(saturate($links, 20%), 20%); +$linksHover: darken(saturate($links, 20%), 20%); +$linksHover-hue-2: transparentize($linksHover, .5); +$linkAlt: $secondary-hue1; +$md-primary: $links; + +$linkTitle: #33FFFF; + +$collectionDisLinks: #0075b0; + +$red: tomato; +// warning +$warning: $red; +$warn: $red; +$warningBorder: inset 0 0 0 3px $warning; + + +$text: $dark; +$text-hue1: lighten($text, 30%); +$text-hue2: $text; +$text-hue3: darken($text, 10%); + +$notice: #B84D00; + +$highlight: $highlightYellow; +$highlight-hue1: #FFFCC4; + +$modalBackdrop: rgb(68,68,68); +$moreLink: #45aab4; + +$selectedItem: saturate(lighten($midGrey, 53%), 40%); + +$citationTitle: rgb(68, 112, 123); +// $citationTitle: $titles; + +// chips +$chipTouchHeight: 28px; +$chipDesktopHeight: 22px; + +// Responsive +$responsiveMaxWidth: 960px !important; + +// Default bars height +$defaultBarHeight: 60px; + +// height of buttons in brief results +$briefButtonHeight: 38px; + +// citations +$citation: tomato; +$citationColor: $citation; +$citation-hue1: darken($citation, 40%); + +// personalization +$personalization: #7d1538; + +// Peer Reviewed +$peer-reviewed-color :#8359d4; + +// Open Access +$open-access-color: #f68212; + +// Angular Material +//----------------- + +// Progress bar +$progressBarHeight: 15px; + +// buttons +$defaultMdButtonHover: rgba(158,158,158,0.2); + + +//// Angular Material overrides +// Angular Material transition easing curves +//----------------------- + +$swift-ease-out-duration: 0.4s !default; +$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default; +$swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function !default; + +$swift-ease-in-duration: 0.3s !default; +$swift-ease-in-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2) !default; +$swift-ease-in: all $swift-ease-in-duration $swift-ease-in-timing-function !default; + +$swift-ease-in-out-duration: 0.5s !default; +$swift-ease-in-out-timing-function: cubic-bezier(0.35, 0, 0.25, 1) !default; +$swift-ease-in-out: all $swift-ease-in-out-duration $swift-ease-in-out-timing-function !default; + +// Angular Material layouts +// ------------------------------ + +$baseline-grid: 8px !default; +$layout-gutter-width: ($baseline-grid * 2) !default; + +// Angular Material breakpoints +// ------------------------------ + +// $layout-breakpoint-xs: 600px !default; +// $layout-breakpoint-sm: 960px !default; +// $layout-breakpoint-md: 1280px !default; +// $layout-breakpoint-lg: 1920px !default; diff --git a/tests/manual/TESTBROWSERIFY/showDirectives.txt b/tests/manual/TESTBROWSERIFY/showDirectives.txt new file mode 100644 index 000000000..4940b4e09 --- /dev/null +++ b/tests/manual/TESTBROWSERIFY/showDirectives.txt @@ -0,0 +1 @@ +javascript:(function(){var script=document.createElement("SCRIPT");script.src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';script.type='text/javascript';document.getElementsByTagName("head")[0].appendChild(script);var checkReady=function(callback){if(window.jQuery){callback(jQuery)}else{window.setTimeout(function(){checkReady(callback)},100)}};checkReady(function($){$('primo-explore').find('[parent-ctrl="$ctrl"]').each(function(){$(this).append('Hover for id')})})})(); diff --git a/tests/manual/TESTCONCAT/README.md b/tests/manual/TESTCONCAT/README.md new file mode 100644 index 000000000..865ec76b3 --- /dev/null +++ b/tests/manual/TESTCONCAT/README.md @@ -0,0 +1,48 @@ + +# The Primo New UI Customization Workflow Development Environment + + +## Package documentation + +The development package allows you to configure : + +- css + +- images + +- html + +- JavaScript + +- The root directory of the package should be named either by the `viewCode` or `CENTRAL_PACKAGE` in case of a consortia level package +- Whether you develop a consortia level package or a view level package the process remains the same +- Once deployed the hierarchy is as follows: + 1. For css - use the cascading ability of css and load the consortia level (CENTRAL_PACKAGE) css first and the view level css afterwards + 2. For images and html - the system checks for every file if it exists in each level - and prefers the view level file if exists + 3. For JavaScript - the two package types define 2 different Angular modules: + - ```var app = angular.module('viewCustom', ['angularLoad']);``` + - ```var app = angular.module('centralCustom', ['angularLoad']);``` + + and loads both of the modules, + +- For each configuration type there is a specified folder in the custom package folder (that can be downloaded form your Primo Back Office) +- In each folder you will find a specific README.md file with recipes/examples. + + [CSS](/VIEW_CODE/css/README.md "css documentation") + + [HTML](/VIEW_CODE/html/README.md "html documentation") + + [Images](/VIEW_CODE/img/README.md "images documentation") + + [JavaScript](/VIEW_CODE/js/README.md "javascript documentation") + +- For `colors.json.txt` instructions - please see [CSS](/VIEW_CODE/css/README.md "css documentation") documentation + + + + + + + + + diff --git a/tests/manual/TESTCONCAT/colors.json b/tests/manual/TESTCONCAT/colors.json new file mode 100644 index 000000000..963f80de6 --- /dev/null +++ b/tests/manual/TESTCONCAT/colors.json @@ -0,0 +1,15 @@ +{ + "primary": "red", + "secondary" : "green", + "backgroundColor" : "yellow", + "links": "#3D6E94", + "warning": "tomato", + "positive": "#0f7d00", + "negative": "gray", + "notice": "#B84D00", + "linkTitle": "#33FFFF", + "citation": "tomato", + "citationTitles": "rgb(68, 112, 123)", + "personalization": "#7d1538" +} + diff --git a/tests/manual/TESTCONCAT/css/README.md b/tests/manual/TESTCONCAT/css/README.md new file mode 100644 index 000000000..5df326507 --- /dev/null +++ b/tests/manual/TESTCONCAT/css/README.md @@ -0,0 +1,168 @@ +# The Primo New UI Customization Workflow Development Environment + + +## css documentation + +- Primo uses Angular Directives massively in this project + +- To learn more about directives see: +> https://docs.angularjs.org/guide/directive + +- Primo uses external directives from the Angular-material framework : +> https://material.angularjs.org/latest/ + +- Those directives are tagged by a prefix : "md-" + +- Primo also creates its own directives which are tagged by the "prm-" prefix. + + +Example: +``` +
+ + + + + + + + + +
+``` + + +- You can see in the example how we use : + +1. An HTML5 tag - header +2. A Primo directive : prm-topbar , prm-search-bar. +3. An external material design directive : md-progress-bar : +> https://material.angularjs.org/latest/api/directive/mdProgressLinear + + + +- When defining css rules it is important to understand the css cascading/specifity logic: + +> http://www.w3.org/TR/css3-cascade/ + +> https://specificity.keegan.st/ + + + + +- When you start working on customizing your css be aware of the ability to define css selectors based on the directive name, which is actually equivalent +to an html tag - this will enable you changing the design of a component cross-system without relying on id's/classes + +- For the example above we can define selectors: + +``` +prm-topbar input {....} +prm-topbar.md-primoExplore-theme input {....} +``` +- Primo is using a theme inside angular-material to define a palette of colors see: +> https://material.angularjs.org/latest/Theming/01_introduction + + +- This means that you will often encounter a class "md-primoExplore-theme" attached to elements. + + + +## Recipes/Examples: + + +# css Recipe 1 - Color Scheme (Starting from August 2016 Release) + +- Open the `colors.json.txt` file in the root of your view folder +- You will see a json object with our default color scheme: + + ``` + { + "primary": "#53738C", + "secondary" : "#A9CDD6", + "backgroundColor" : "white", + "links": "#5C92BD", + "warning": "tomato", + "positive": "#0f7d00", + "negative": "gray", + "notice": "#e08303" + } + ``` + +- Since November 2016 release - we are giving you the ability to easily customize the majority of the following +colors - primary, secondary, backgroundColor, links, warning, positive, negative, notice - just change the definition and save the file. + +The colors are mapped to different elements in the user interface: + +![Color Changes image](../../help_files/colors3.png "Color Changes") + +- Open a new command line window + +- cd to the project base directory (C:\**\**\primo-explore-devenv) +- Run `gulp app-css --view ` for example: + `gulp app-css --view Auto1` +- for Primo Ve customers add the --ve flag at the end of the command for example: + `gulp app-css --view Auto1 --ve` +- A new file will be created on your package css directory named: `app-colors.css` +- This file will contain all of the primo-explore theme color definitions. + We will continue to add more color definitions to extend this ability +- Refresh your browser to see the changes take affect +- For example, for the following `colors.json.txt` file: + +``` +{ + "primary": "#512DA8", + "secondary" : "#D1C4E9", + "backgroundColor" : "#BDBDBD", + "links": "#009688", + "warning": "#FF5722" +} + + +``` + +You will get: + + ![Color Changes image](../../help_files/colors1.png "Color Changes") + + ![Color Changes image](../../help_files/colors2.png "Color Changes") + +# css Recipe 2 - Moving the Facets to the Left + + +- Select the parent container containing the search result and the facets +- Copy the selector definition using your browsers' dev tools +- Define the container as + +``` +display:flex; +flex-flow:row-reverse; +``` + + +- complete css definition: +``` +prm-search > md-content.md-primoExplore-theme .main, prm-search > md-content.md-primoExplore-theme.main { + display: -webkit-flex; !* Safari *! + -webkit-flex-flow: row-reverse wrap; !* Safari 6.1+ *! + display: flex; + flex-flow: row-reverse wrap; + +} +.screen-gt-sm .sidebar{ + webkit-flex: 0 0 15%; + flex: 0 0 15%; +} +``` +- Save and refresh your browser + +- The result: + + + ![Facets image](../../help_files/facets.png "Factes Changes") + + + + + + + diff --git a/tests/manual/TESTCONCAT/css/app-colors.css b/tests/manual/TESTCONCAT/css/app-colors.css new file mode 100644 index 000000000..e69de29bb diff --git a/tests/manual/TESTCONCAT/css/custom1.css b/tests/manual/TESTCONCAT/css/custom1.css new file mode 100644 index 000000000..e69de29bb diff --git a/tests/manual/TESTCONCAT/html/README.md b/tests/manual/TESTCONCAT/html/README.md new file mode 100644 index 000000000..3f8d98b15 --- /dev/null +++ b/tests/manual/TESTCONCAT/html/README.md @@ -0,0 +1,152 @@ +# The Primo New UI Customization Workflow Development Environment + + +## html documentation + + - In this folder you will find static html files in their OTB state + - The files are separated into directories (starting from the November 2016 Release) + - You can edit the html to comply with your library requirements + - To support multiple languages in your interface just add a suffix with the language code to your file-name, + For example: + + 1. homepage_fe_FR.html (in the August 2016 Release use home_fe_FR.html) + 2. help_de_DE.html(Available in the November 2016 Release) + + - This is how your directory structure should look like: + + ![August 2016 Release structure image](../../help_files/htmlStructureAug.png "August 2016 Release structure") + + + - Note that you can use Angular Material directives in your html: + > https://material.angularjs.org/latest/ + + +## Email templates + Starting from 2020 Primo supports a new html template to customize email messages sent to patrons'. + + To customize this template create a file named email_fr_FR.html (based on your language suffix) and upload it using the html directory in your customization package. + + In this html file you can design your own email template using html. + + Since Primo supports the email action on multiple records you should add the following attribute to the html element represnting a record: + + ```ng-if="$ctrl.parentCtrl.fullViewLoaded" ng-repeat="item in $ctrl.parentCtrl.delayedItems"``` + + for example: + + For an email template layout as below the attribute should be added to the records container: + + ``` +
+ + +
+ + + + + + ... + +
+
+
+ ``` + + When editing the template you can: + + 1. Use regular html (use best practices for email templating: https://mailchimp.com/help/about-html-email/) to design the layout of your email based on your preferences. + + 2. You can take advantage of some of our OTB directives in your templates: +``` + + + + + + + + + ``` + 3. You can reference the sent item(pnx/record) using angular syntax to present the relevant data: + + use the curly brackets to access the pnx diectly - for example: +``` + {{item.pnx.display.title}} +``` + or loop over values using the ng-repeat directive to add multiple fields: + +```
+
Additional Information From the Record:
+ OCLCID: +
{{oclcid}}
+``` +``` ``` + + + + + + +**Notice :** Any link in your template will be removed - for security reasons, we will not allow links other than the link to the record to avoid email exploits. + +To add the link to the full record you can either: + 1. Use the + + ``` + + + ``` +directive in your template - This includes the link to the record + +2. Use the following code snippet in your template: + + ``` + + {{item.pnx.display.title[0]}} + + ``` + +To use your library logo (not the OTB ) you can use your own html, with an img element that has a class attribute of *logo-image* immediatly following the opening tag, for example: + + ``` + + + + + + +
+ +
+``` + + + ## Examples + + Full examples of email templates can be found in the help files folder folder: + + 1. [The Out of the Box template for emails](../../help_files/email_en_US.html) email_en_US.html + + 2. [A template based on the OTB - brief + custom fields + availability](../../help_files/email_en_US-brief+additionalField+availability.html) + will produce the following email: + + ![example brief](../../help_files/example-bried-and-subject.png "example brief") + + 3. [A template based on an open source email template with no Primo directives - just use of the ```{{item.pnx.display.title}}```](../../help_files/email_en_US_Details.html) + will produce the following email: + + ![example external](../../help_files/example-external.png "example external") + + + + diff --git a/tests/manual/TESTCONCAT/html/home_en_US.html b/tests/manual/TESTCONCAT/html/home_en_US.html new file mode 100644 index 000000000..96b69aa8e --- /dev/null +++ b/tests/manual/TESTCONCAT/html/home_en_US.html @@ -0,0 +1 @@ +Customized diff --git a/tests/manual/TESTCONCAT/img/README.md b/tests/manual/TESTCONCAT/img/README.md new file mode 100644 index 000000000..eb4c0bb82 --- /dev/null +++ b/tests/manual/TESTCONCAT/img/README.md @@ -0,0 +1,20 @@ +# The Primo New UI Customization Workflow Development Environment + + +## images documentation + + - Primo allows the customization of the following images; + 1. Library Logo - just place a file named `library-logo.png` in this location + 2. Library favicon - just place a file named `favicon.ico` in this location + 3. Default Resource Types - just place a files following this convention : + `icon_.png` + + For Example: + `icon_book.png` + + + + + + + diff --git a/tests/manual/TESTCONCAT/img/icon_book.png b/tests/manual/TESTCONCAT/img/icon_book.png new file mode 100644 index 000000000..048bdfe88 Binary files /dev/null and b/tests/manual/TESTCONCAT/img/icon_book.png differ diff --git a/tests/manual/TESTCONCAT/img/library-logo.png b/tests/manual/TESTCONCAT/img/library-logo.png new file mode 100644 index 000000000..4889cb614 Binary files /dev/null and b/tests/manual/TESTCONCAT/img/library-logo.png differ diff --git a/tests/manual/TESTCONCAT/js/README.md b/tests/manual/TESTCONCAT/js/README.md new file mode 100644 index 000000000..cfc360d7a --- /dev/null +++ b/tests/manual/TESTCONCAT/js/README.md @@ -0,0 +1,275 @@ +# The Primo New UI Customization Workflow Development Environment + + +## JavaScript documentation + +- When you want to add functionality to your Primo installation you will be using Angular Directives. + +- To learn more about directives see: +> https://docs.angularjs.org/guide/directive + +- Primo uses external directives from the Angular-material framework: +> https://material.angularjs.org/latest/ + +- Those directives are tagged by a prefix : "md-" + +- Primo also creates its own directives which are tagged by the "prm-" prefix. + + +Example: +``` +
+ + + + + + + + + +
+``` + + +- You can see in the example how we use : + +1. An HTML5 tag - header +2. A Primo directive : prm-topbar , prm-search-bar. +3. An external material design directive : md-progress-bar : +> https://material.angularjs.org/latest/api/directive/mdProgressLinear + + + +## Concept + +- When You want to add your own JavaScript functionality - you will need to plug-in to placeholder Directives we added to the system, by creating your own placeholder Directive. +- Those directives are added as the last child element for every Primo directive (defined by the `prm-` prefix) +- The placeholder directives are injected (as input) with the Controller of their parent, thus have access to the data model of the parent directive +- Use the examples below as starting points for your JavaScript plug-in directives +- Learn about Angular Directives to better understand the different abilities this workflow offers +> https://docs.angularjs.org/guide/directive + + + +## Recipes/Examples: + +# Note: + +The examples below use the back tic '`' for templates - which will work using babel (Documentation on how to do so will be shared) +This causes the examples to fail on IE11 browsers. + +To solve this you can replace the '`' with regular apostrophe ("'") and use a single line template (less readable but works just as well). + + + + +# JavaScript Recipe 1 - a Static `hello world` html Message + + +- Use the `showDirectives` (located in the root directory of this package is the showDirectives.txt file +, just add the content of the file as a bookmark to your browser) scriplet to identify the `prmSearchBarAfter` directive which you will plugin to + + +![Show Directives image](../../help_files/showDirectives.png "Show Directives Changes") + +- Edit the primo-explore/custom/js/custom.js file and add a component declaration for `myInstitutionComponent` and a component declaration +for the `prmSearchBarAfter` directive + + ``` + app.component('myInstitutionComponent', { + + + }); + + app.component('prmSearchBarAfter', { + + + }); + ``` +- Add the template property with your static message + + ``` + app.component('myInstitutionComponent', { + template: `Hello World` + }); + ``` +- Add the template property with your component template (myInstitutionComponent) + + ``` + app.component('prmSearchBarAfter', { + bindings: {parentCtrl: `<`}, + template: `` + }); + ``` + + +- Save and refresh your browser + +![Hello World image](../../help_files/js1.png "Hello World Changes") + +# JavaScript Recipe 2 - a Dynamic Directive +- Use the `showDirectives` scriplet to identify the `prmSearchBarAfter` directive which you will plugin to +- Run the following command in your browsers' console tab: +``` + angular.reloadWithDebugInfo() +``` +- Focus on the `prmSearchBarAfter` directive + +![Focus image](../../help_files/js2.png "Focus") + +- Run the following command in your browsers' console tab: +``` + angular.element($0).scope().$ctrl (angular.element($0).scope().ctrl in version earlier than February 2017) +``` + +- Review the properties of the directive to decide which data elements can be used, avoid methods/functions as they wont be backwards compatible + +![properties image](../../help_files/js3.png "properties") + + +- Edit primo-explore/custom/js/custom.js file and add: a component declaration for the `prmSearchBarAfter` directive, and a component declaration for the `myInstitutionComponent` + +- Add a binding definition the input parentCtrl for both components +``` + bindings: {parentCtrl: '<'}, +``` +- Add a controller definition for the `myInstitutionComponent` component: +``` + controller: 'MyInstitutionComponentController', +``` +- Define a controller with 2 getter methods to return the query and selected scope +``` +app.controller('MyInstitutionComponentController', [function () { + var vm = this; + + + vm.getSelectdScope = getSelectdScope; + vm.getQuery = getQuery; + + + function getSelectdScope() { + return vm.parentCtrl.scopeField; + } + + function getQuery() { + return vm.parentCtrl.mainSearchField; + } + }]); + +``` +- Edit the `prmSearchBarAfter` directive template to reference the `myInstitutionComponent` +``` +template: `` +``` +- Edit the `myInstitutionComponent` directive template to reference the getter methods +``` +template: `
+ + + + This is a demo presenting the ability to display query + information below the search box + Query: {{$ctrl.getQuery()}} + Scope: {{$ctrl.getSelectdScope()}} + + +
+
+
+ + Action 1 + Action 2 + +
+
` + + ``` +- Save and refresh your browser + +![dynamic example image](../../help_files/js4.png "dynamic example") + +# JavaScript Recipe 3 - Adding the Altmetrics Widget +- Use the `showDirectives` scriplet to identify the `prmFullViewAfter` directive which you will plugin to + +![Altmetrics example image](../../help_files/js5.png "Altmetrics example") + +- Run the following command in your browsers' console tab: +`angular.reloadWithDebugInfo()` +- Focus on the `prmFullViewAfter` directive +- Run the following command in your browsers' console tab: +`angular.element($0).scope().ctrl` + +- Review the properties of the directive to decide which data elements can be used, avoid methods/functions as they wont be backwards compatible + +![Altmetrics example 2 image](../../help_files/js6.png "Altmetrics 2 example") + +- Edit primo-explore/custom/js/custom.js file and add: a component declaration for the `prmFullViewAfter` directive, and a component declaration for the `myInstitutionComponent` + +- Add a binding definition the input parentCtrl for both components +``` + bindings: {parentCtrl: '<'}, +``` +- Add a controller definition for the `myInstitutionComponent` component: +``` + controller: 'MyInstitutionComponentController', +``` +- Define a controller that populates the doi and loads the Altmetrics js file +``` +app.controller('MyInstitutionComponentController', ['angularLoad', function (angularLoad) { + var vm = this; + vm.doi = vm.parentCtrl.item.pnx.addata.doi[0] || ''; + + vm.$onInit = function () { + angularLoad.loadScript('https://d1bxh8uas1mnw7.cloudfront.net/assets/embed.js?' + Date.now()).then(function () { + + }); + }; + }]); +``` +- Edit the `prmFullViewAfter` directive template to reference the `myInstitutionComponent` +``` +template: `` +``` +- Edit the `myInstitutionComponent` directive template to add the Altmetrics div and bind the data-doi attribute to the controller +``` +app.component('myInstitutionComponent', { + bindings: {parentCtrl: '<'}, + controller: 'MyInstitutionComponentController', + template: `
+
+
+

+ Social Popularity Statistics (AltMetrics) : +

+ + +
+
+
+
+
+
+
+
+
+
+
+
+
` + }); + ``` + +- Edit the custom1.css file and add the following definitions: + + ``` + .full-view-section.loc-altemtrics{ + background-color: #f3f3f3; + margin-top:0px; + padding-left: 3em; + } + ``` + +- Save and refresh your browser + +![Altmetrics example 3 image](../../help_files/js7.png "Altmetrics 3 example") diff --git a/tests/manual/TESTCONCAT/js/custom.js b/tests/manual/TESTCONCAT/js/custom.js new file mode 100644 index 000000000..b1c2ca3d6 --- /dev/null +++ b/tests/manual/TESTCONCAT/js/custom.js @@ -0,0 +1,42 @@ +(function(){ +"use strict"; +'use strict'; + +var app = angular.module('viewCustom', ['angularLoad']); + +app.service('TestService', [function () { + var vm = this; + + vm.manipulate = manipulate; + + function manipulate(str) { + return str.split("").reverse().join(""); + } +}]); + +app.component('myInstitutionComponent', { + bindings: { parentCtrl: '<' }, + controller: 'MyInstitutionComponentController', + template: '
\n \n \n \n This is a demo presenting the ability to display query\n information below the search box\n Query: {{$ctrl.getQuery()}}\n Scope: {{$ctrl.getSelectdScope()}}\n \n \n
\n
\n
\n \n Action 1\n Action 2\n \n
\n
\n ' +}); + +app.component('prmSearchBarAfter', { + bindings: { parentCtrl: '<' }, + template: '' +}); + +app.controller('MyInstitutionComponentController', ['TestService', function (testService) { + var vm = this; + + vm.getSelectdScope = getSelectdScope; + vm.getQuery = getQuery; + + function getSelectdScope() { + return testService.manipulate(vm.parentCtrl.scopeField); + } + + function getQuery() { + return vm.parentCtrl.mainSearchField; + } +}]); +})(); \ No newline at end of file diff --git a/tests/manual/TESTCONCAT/js/custom.module.js b/tests/manual/TESTCONCAT/js/custom.module.js new file mode 100644 index 000000000..80a8a536b --- /dev/null +++ b/tests/manual/TESTCONCAT/js/custom.module.js @@ -0,0 +1 @@ +var app = angular.module('viewCustom', ['angularLoad']); diff --git a/tests/manual/TESTCONCAT/js/test.service.js b/tests/manual/TESTCONCAT/js/test.service.js new file mode 100644 index 000000000..3579f665b --- /dev/null +++ b/tests/manual/TESTCONCAT/js/test.service.js @@ -0,0 +1,11 @@ +app.service('TestService', [function () { + let vm = this; + + + vm.manipulate = manipulate; + + + function manipulate(str){ + return str.split("").reverse().join(""); + } +}]); diff --git a/tests/manual/TESTCONCAT/js/testjs.js b/tests/manual/TESTCONCAT/js/testjs.js new file mode 100644 index 000000000..04a5c6e30 --- /dev/null +++ b/tests/manual/TESTCONCAT/js/testjs.js @@ -0,0 +1,50 @@ +app.component('myInstitutionComponent', { + bindings: {parentCtrl: '<'}, + controller: 'MyInstitutionComponentController', + template: `
+ + + + This is a demo presenting the ability to display query + information below the search box + Query: {{$ctrl.getQuery()}} + Scope: {{$ctrl.getSelectdScope()}} + + +
+
+
+ + Action 1 + Action 2 + +
+
+ ` +}); + + + + + +app.component('prmSearchBarAfter', { + bindings: {parentCtrl: `<`}, + template: `` +}); + +app.controller('MyInstitutionComponentController', ['TestService', function (testService) { + var vm = this; + + + vm.getSelectdScope = getSelectdScope; + vm.getQuery = getQuery; + + + function getSelectdScope() { + return testService.manipulate(vm.parentCtrl.scopeField); + } + + function getQuery() { + return vm.parentCtrl.mainSearchField; + } +}]); diff --git a/tests/manual/TESTCONCAT/scss/partials/_variables.scss b/tests/manual/TESTCONCAT/scss/partials/_variables.scss new file mode 100644 index 000000000..c70d2883c --- /dev/null +++ b/tests/manual/TESTCONCAT/scss/partials/_variables.scss @@ -0,0 +1,225 @@ +//// VARIABLES +// The complex color calculation were created with http://razorjam.github.io/sasscolourfunctioncalculator/ + +// Fonts family +$serif: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif; + +// Font weights +$light: 300; +$normal: 400; +$bold: 600; +$heavy: 900; + +// Font size +$defaultFontSize: 15px; + +// Shapes +$radius: 3px; + +// Padding +$paddingBase: 1em; +$paddingExtra: 1.5em; +$paddingMedium: 1.3em; +$paddingLarge: 1em; +$paddingSmall: .5em; +$paddingTiny: .25em; + +// colors + +$muteOrange: red; + + + +$almostBlack: #777; + + + +$titles: rgb(68, 112, 123); +$frbr: #ed9100; +$pink: rgb(255, 171, 200); + +$dark: #3a3a3a; +$darker: darken(saturate(adjust-hue($dark, 0.0000), 0.0000), 6.6667); + +// lighter background color + + +$highlightYellow: #FFFCC4; //stronger +$highlight: darken(desaturate(adjust-hue($highlightYellow, -4.0920), 16.0000), 8.0392); +$highlightText: #ffe564; +$success: #3EA03E; +//$green: #3EA03E; // redundancy of 'success' +$green: #198A19; +//$orange: #CE8015; //#E0962E; +$muteOrange: #C59655; +$yellow: #F1C16F; +$overlayDarkDG: rgba(68, 68, 68, 0.6); + +// $availabiltyDetails: desaturate(lighten($midGrey,5%), 5%); +//$maybe: $orange; + +$alert: #F7EDA3; +$alert-hue1: darken(desaturate(adjust-hue($alert, 0.3074), 15.3043), 2.9412); + + +$disabled: #aaa; +$courseColor: #55909B; + +$primary: red; +$primary-hue1: darken($primary, 3%); +$primary-hue2: darken($primary, 5%); +$primary-hue3: darken($primary-hue2, 2%); +$primary-hue4: darken($primary, 10%); +$primary-hue5: darker($primary, -5%); + +$secondary: green; +$secondary-hue1: darken(saturate(adjust-hue($secondary, -6.5946), 9.1452), 26.2745); +$secondary-hue2: darken($secondary, 20%); +$secondary-hue3:darken(saturate(adjust-hue($secondary, -12), 30.28), 47.65); // "show more" buttons in facets +$secondary-hue4: darken(saturate(adjust-hue($secondary, -7), 64.57), 13.92); + + +$backgroundColor: yellow; + +$positive: #0f7d00; + +$negative: gray; + +$background: darken(saturate(adjust-hue($backgroundColor, 0.0000), 0.0000), 8.6275); //redundancy of almostwhite + +$background-hue1: darken($backgroundColor, 3%); // darker 1 (i.e: background for locations filter bar) +// $background-hue2: lighten(saturate(adjust-hue($backgroundColor, 0.0000), 0.0000), 3.9216); // lighter 1 (i.e: background for full view dialog) +$background-hue2: lighten($background, 4%); +$background-hue3: lighten(saturate(adjust-hue($backgroundColor, 0.0000), 0.0000), 5.8824); // lighter 2 (i.e: background for actions forms) + +$background-hue4: lighten($background, 3%); +$background-hue5: transparentize($background, .5); +$background-hue6: darken($background, 1%); +$background-hue7: darken($background, 10%); +$background-hue8: lighten($background, 6%); +$background-hue9: darken($background, 5%); +$background-hue10: darken($background, 12%); +$background-hue11: darken($background, 2%); +$background-hue12: darken($background, 7%); +$background-hue13: lighten($background, 2%); + +$white: white; + +$almostWhite: lighten(saturate(adjust-hue($background, 60.0000), 20.0000), 0.7843); +$bg: $background; +$nearlyWhite: darken(saturate(adjust-hue($backgroundColor, 0.0000), 0.0000), 5.4902); // stronger +$blueGrey: #ccc; +$midGrey: #53738C; //darken(saturate(adjust-hue($blueGrey, 206.3158), 25.5605), 36.2745); +$actionsBackground: lighten($background, 6%); +$autoCompleteBg: $backgroundColor; +$sectionHighlight: darken(desaturate($background, 0), 5%); +$snippets:#999; +$lightenGrey: #6d6d6d; + +$links: #3D6E94; + +$links-background-hue1: transparentize($links, 1); + +$links: $links; +$links-hue-1: darken(saturate($links, 5%), 5%); +$links-hue-5: darken(saturate($links, 20%), 20%); +$linksHover: darken(saturate($links, 20%), 20%); +$linksHover-hue-2: transparentize($linksHover, .5); +$linkAlt: $secondary-hue1; +$md-primary: $links; + +$linkTitle: #33FFFF; + +$collectionDisLinks: #0075b0; + +$red: tomato; +// warning +$warning: $red; +$warn: $red; +$warningBorder: inset 0 0 0 3px $warning; + + +$text: $dark; +$text-hue1: lighten($text, 30%); +$text-hue2: $text; +$text-hue3: darken($text, 10%); + +$notice: #B84D00; + +$highlight: $highlightYellow; +$highlight-hue1: #FFFCC4; + +$modalBackdrop: rgb(68,68,68); +$moreLink: #45aab4; + +$selectedItem: saturate(lighten($midGrey, 53%), 40%); + +$citationTitle: rgb(68, 112, 123); +// $citationTitle: $titles; + +// chips +$chipTouchHeight: 28px; +$chipDesktopHeight: 22px; + +// Responsive +$responsiveMaxWidth: 960px !important; + +// Default bars height +$defaultBarHeight: 60px; + +// height of buttons in brief results +$briefButtonHeight: 38px; + +// citations +$citation: tomato; +$citationColor: $citation; +$citation-hue1: darken($citation, 40%); + +// personalization +$personalization: #7d1538; + +// Peer Reviewed +$peer-reviewed-color :#8359d4; + +// Open Access +$open-access-color: #f68212; + +// Angular Material +//----------------- + +// Progress bar +$progressBarHeight: 15px; + +// buttons +$defaultMdButtonHover: rgba(158,158,158,0.2); + + +//// Angular Material overrides +// Angular Material transition easing curves +//----------------------- + +$swift-ease-out-duration: 0.4s !default; +$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default; +$swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function !default; + +$swift-ease-in-duration: 0.3s !default; +$swift-ease-in-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2) !default; +$swift-ease-in: all $swift-ease-in-duration $swift-ease-in-timing-function !default; + +$swift-ease-in-out-duration: 0.5s !default; +$swift-ease-in-out-timing-function: cubic-bezier(0.35, 0, 0.25, 1) !default; +$swift-ease-in-out: all $swift-ease-in-out-duration $swift-ease-in-out-timing-function !default; + +// Angular Material layouts +// ------------------------------ + +$baseline-grid: 8px !default; +$layout-gutter-width: ($baseline-grid * 2) !default; + +// Angular Material breakpoints +// ------------------------------ + +// $layout-breakpoint-xs: 600px !default; +// $layout-breakpoint-sm: 960px !default; +// $layout-breakpoint-md: 1280px !default; +// $layout-breakpoint-lg: 1920px !default; diff --git a/tests/manual/TESTCONCAT/showDirectives.txt b/tests/manual/TESTCONCAT/showDirectives.txt new file mode 100644 index 000000000..4940b4e09 --- /dev/null +++ b/tests/manual/TESTCONCAT/showDirectives.txt @@ -0,0 +1 @@ +javascript:(function(){var script=document.createElement("SCRIPT");script.src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';script.type='text/javascript';document.getElementsByTagName("head")[0].appendChild(script);var checkReady=function(callback){if(window.jQuery){callback(jQuery)}else{window.setTimeout(function(){checkReady(callback)},100)}};checkReady(function($){$('primo-explore').find('[parent-ctrl="$ctrl"]').each(function(){$(this).append('Hover for id')})})})(); diff --git a/tests/manual/test-plan.md b/tests/manual/test-plan.md new file mode 100644 index 000000000..0f3b69f15 --- /dev/null +++ b/tests/manual/test-plan.md @@ -0,0 +1,51 @@ + + + +1. copy default package from: + https://github.com/ExLibrisGroup/primo-explore-package + + 1.1 Installation + 1.2 test concat: + + gulp run --view TESTCONCAT + gulp run --view TESTCONCAT --ve + + 1.2.1 test colors.json + + 1.2.2 test css + + 1.2.3 test images + + 1.2.4 test html + + 1.2.5 test javascript component + + 1.2.6 test create-package + + 1.2.7 uplocag to BO and test deployment + + 1.3 test browserify: + + gulp run --view TESTBROWSERIFY --browserify + + gulp run --view TESTBROWSERIFY --browserify --proxy https://***:443 --ve + + 1.3.1 test colors.json + + 1.3.2 test scss + + 1.3.3 test images + + 1.3.4 test html + + 1.3.5 test javascript component in ts + + 1.3.6 test create-package + + 1.3.7 uplocag to BO and test deployment + + + + +REPEAT WITH --VE +