├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE.txt ├── README.md ├── package-lock.json ├── package.json ├── slushfile.js ├── templates ├── README.md ├── _bowerrc ├── _editorconfig ├── _gitignore ├── bower.json ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── gulpfile.js ├── mlcp │ └── log4j.properties ├── package.json └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── example │ │ │ ├── App.java │ │ │ ├── Config.java │ │ │ ├── DevConfig.java │ │ │ ├── MimeMappingConfig.java │ │ │ ├── client │ │ │ ├── DigestAuthenticationManager.java │ │ │ ├── DigestRestClient.java │ │ │ ├── DigestRestClientSession.java │ │ │ └── DigestRestTemplateLoader.java │ │ │ ├── util │ │ │ ├── ModuleWatcher.java │ │ │ └── URIUtil.java │ │ │ └── web │ │ │ ├── AppController.java │ │ │ └── UploadController.java │ ├── ml-config │ │ ├── README.md │ │ ├── cpf │ │ │ ├── cpf-configs │ │ │ │ └── README.md │ │ │ ├── domains │ │ │ │ └── README.md │ │ │ └── pipelines │ │ │ │ └── README.md │ │ ├── databases │ │ │ ├── README.md │ │ │ ├── content-database.json │ │ │ └── modules-database.json │ │ ├── forests │ │ │ └── README.md │ │ ├── groups │ │ │ ├── README.md │ │ │ └── default-group.xml │ │ ├── rest-api.json │ │ ├── security │ │ │ ├── amps │ │ │ │ └── README.md │ │ │ ├── external-security │ │ │ │ └── README.md │ │ │ ├── privileges │ │ │ │ └── README.md │ │ │ ├── protected-collections │ │ │ │ └── README.md │ │ │ ├── roles │ │ │ │ ├── README.md │ │ │ │ └── sample-app-role.json │ │ │ └── users │ │ │ │ ├── README.md │ │ │ │ └── sample-project-default-user.json │ │ ├── servers │ │ │ └── README.md │ │ ├── tasks │ │ │ └── README.md │ │ └── view-schemas │ │ │ └── README.md │ ├── ml-content │ │ ├── CONTENTS-ARE-NOT-AUTO-DEPLOYED.md │ │ ├── api │ │ │ └── users │ │ │ │ ├── admin.json │ │ │ │ └── sample-project-default-user.json │ │ ├── dictionary-large.xml │ │ └── sample-data.zip │ ├── ml-modules │ │ ├── ext │ │ │ ├── constraint │ │ │ │ └── geo.xqy │ │ │ └── corb │ │ │ │ ├── transform.xqy │ │ │ │ └── uris.xqy │ │ ├── options │ │ │ └── all.xml │ │ ├── root │ │ │ ├── config │ │ │ │ └── config.xqy │ │ │ ├── lib │ │ │ │ └── utilities.xqy │ │ │ ├── login-check.xqy │ │ │ └── robots.txt │ │ ├── services │ │ │ ├── analyze-data.xqy │ │ │ ├── extsimilar.xqy │ │ │ └── extspell.xqy │ │ └── transforms │ │ │ ├── download.xsl │ │ │ ├── extractedToJson.sjs │ │ │ ├── filter-docs.xqy │ │ │ ├── from-json.xsl │ │ │ ├── get-property.xsl │ │ │ ├── indent.xsl │ │ │ ├── sanitize.xsl │ │ │ └── to-json.xsl │ ├── resources │ │ ├── application.properties │ │ └── logback.xml │ └── webapp │ │ ├── app │ │ ├── app.js │ │ ├── create │ │ │ ├── create.controller.js │ │ │ ├── create.controller.spec.js │ │ │ ├── create.html │ │ │ └── create.module.js │ │ ├── detail │ │ │ ├── detail.controller.js │ │ │ ├── detail.controller.spec.js │ │ │ ├── detail.html │ │ │ ├── detail.module.js │ │ │ ├── ml-similar.directive.js │ │ │ ├── ml-similar.directive.spec.js │ │ │ ├── ml-similar.html │ │ │ └── ml-similar.module.js │ │ ├── error │ │ │ ├── error.module.js │ │ │ ├── errorInterceptor.service.js │ │ │ └── errorInterceptor.service.spec.js │ │ ├── landing │ │ │ ├── landing.controller.js │ │ │ ├── landing.controller.spec.js │ │ │ ├── landing.html │ │ │ └── landing.module.js │ │ ├── login │ │ │ ├── login-full.html │ │ │ ├── login-modal.html │ │ │ ├── login.controller.js │ │ │ ├── login.directive.js │ │ │ ├── login.html │ │ │ ├── login.module.js │ │ │ ├── login.service.js │ │ │ ├── login.service.spec.js │ │ │ └── loginInterceptor.service.js │ │ ├── message-board │ │ │ ├── message-board-dir.html │ │ │ ├── message-board.directive.js │ │ │ ├── message-board.html │ │ │ ├── message-board.module.js │ │ │ └── message-board.service.js │ │ ├── root │ │ │ ├── root.controller.js │ │ │ ├── root.controller.spec.js │ │ │ ├── root.html │ │ │ └── root.module.js │ │ ├── route │ │ │ ├── route.config.js │ │ │ └── route.module.js │ │ ├── search │ │ │ ├── ml-snippet.directive.js │ │ │ ├── ml-snippet.directive.spec.js │ │ │ ├── ml-snippet.html │ │ │ ├── ml-snippet.module.js │ │ │ ├── search-results.html │ │ │ ├── search.controller.js │ │ │ ├── search.controller.spec.js │ │ │ ├── search.html │ │ │ └── search.module.js │ │ ├── upload │ │ │ ├── ml-uploader.js │ │ │ ├── upload.html │ │ │ ├── uploadForm.html │ │ │ ├── uploader.controller.js │ │ │ └── uploader.module.js │ │ └── user │ │ │ ├── ml-user.directive.js │ │ │ ├── ml-user.directive.spec.js │ │ │ ├── ml-user.html │ │ │ ├── profile.controller.js │ │ │ ├── profile.controller.spec.js │ │ │ ├── profile.html │ │ │ ├── user.module.js │ │ │ ├── user.service.js │ │ │ └── user.service.spec.js │ │ ├── error.html │ │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── images │ │ ├── MarkLogic-Powered-By.png │ │ ├── MarkLogic-white.png │ │ ├── facebook.svg │ │ ├── favicon.ico │ │ ├── favicons │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── browserconfig.xml │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── manifest.json │ │ │ ├── mstile-150x150.png │ │ │ └── safari-pinned-tab.svg │ │ ├── flickr.svg │ │ ├── google.svg │ │ ├── linkedin.svg │ │ ├── logo.jpg │ │ ├── logo.png │ │ ├── marklogic.png │ │ ├── twitter.svg │ │ └── youtube.svg │ │ ├── index.html │ │ ├── login.html │ │ ├── robots.txt │ │ └── styles │ │ ├── admin.less │ │ ├── default.less │ │ ├── main.less │ │ ├── mlu.less │ │ └── theme.less │ └── test │ └── java │ └── org │ └── example │ ├── AbstractAppTest.java │ ├── AppNamespaceProvider.java │ ├── TestConfig.java │ └── WriteAndReadDocumentTest.java └── themes ├── 3column └── src │ └── main │ └── webapp │ ├── app │ ├── landing │ │ └── landing.html │ └── root │ │ └── root.html │ ├── images │ └── filetype_sprite.png │ └── styles │ └── theme.less ├── cards └── src │ └── main │ └── webapp │ ├── app │ ├── detail │ │ └── detail.html │ ├── root │ │ └── root.html │ └── search │ │ ├── search-results.html │ │ ├── search.controller.js │ │ └── search.html │ └── styles │ └── theme.less ├── dashboard └── src │ └── main │ └── webapp │ ├── app │ ├── dashboard │ │ ├── content-box.directive.spec.js │ │ ├── content-box.js │ │ └── dashboard.module.js │ ├── landing │ │ ├── landing.controller.js │ │ ├── landing.controller.spec.js │ │ ├── landing.html │ │ └── landing.module.js │ ├── navigation │ │ ├── navigation.module.js │ │ ├── navigation.service.js │ │ └── navigation.service.spec.js │ └── root │ │ ├── root.controller.js │ │ ├── root.controller.spec.js │ │ ├── root.html │ │ └── root.module.js │ └── styles │ ├── adminLTE.css │ └── theme.less └── map └── src └── main └── webapp ├── app ├── create │ └── create.html ├── detail │ ├── detail.controller.js │ ├── detail.controller.spec.js │ ├── detail.html │ └── detail.module.js ├── landing │ └── landing.html ├── login │ └── login-full.html ├── map │ ├── map-manager.service.js │ ├── map-manager.service.spec.js │ ├── map-utils.service.js │ ├── map-utils.service.spec.js │ └── map.module.js ├── root │ ├── infoWindow-details.html │ ├── infoWindow.html │ ├── root.controller.js │ ├── root.controller.spec.js │ ├── root.html │ └── root.module.js ├── search │ ├── search.controller.js │ ├── search.controller.spec.js │ ├── search.html │ └── search.module.js └── user │ └── profile.html ├── images ├── blue-cluster-marker.png ├── blue-dot-marker.png ├── green-cluster-marker.png ├── green-dot-marker.png ├── grey-cluster-marker.png ├── grey-dot-marker.png ├── purple-cluster-marker.png ├── purple-dot-marker.png ├── red-cluster-marker.png └── red-dot-marker.png └── styles └── theme.less /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | temp 3 | *.log 4 | .idea 5 | templates/.gradle 6 | .gradle 7 | *.iml 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | temp 3 | *.log 4 | .idea 5 | .gradle 6 | bin 7 | build 8 | .settings 9 | .classpath 10 | .project 11 | 12 | # don't ignore .npmignore files 13 | !.npmignore 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v0.1.0: 2 | date: 2016-3-12 3 | changes: 4 | - Initial release. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slush-marklogic-spring-boot", 3 | "description": "Generator for Angular/Spring Boot/MarkLogic apps", 4 | "version": "1.0.0-rc2", 5 | "homepage": "https://github.com/rjrudin/slush-marklogic-spring-boot", 6 | "author": { 7 | "name": "Rob Rudin", 8 | "email": "rjrudin@gmail.com" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/rjrudin/slush-marklogic-spring-boot.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/rjrudin/slush-marklogic-spring-boot/issues" 16 | }, 17 | "license": "MIT", 18 | "main": "slushfile.js", 19 | "engines": { 20 | "node": ">= 0.10.26", 21 | "npm": ">=1.4.3" 22 | }, 23 | "scripts": { 24 | "test": "echo \"No tests\"" 25 | }, 26 | "dependencies": { 27 | "gulp": "^3.9.1", 28 | "gulp-chmod": "^1.3.0", 29 | "gulp-conflict": "^0.1.2", 30 | "gulp-if": "^2.0.0", 31 | "gulp-install": "^0.1.8", 32 | "gulp-rename": "^1.2.2", 33 | "gulp-template": "^0.1.2", 34 | "iniparser": "^1.0.5", 35 | "inquirer": "^0.8.5", 36 | "slush": "^1.1.1", 37 | "underscore.string": "^2.4.0" 38 | }, 39 | "keywords": [ 40 | "slushgenerator", 41 | "marklogic", 42 | "springboot" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /templates/_bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/main/resources/static/bower_components" 3 | } -------------------------------------------------------------------------------- /templates/_editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [*.java] 16 | indent_size = 4 17 | -------------------------------------------------------------------------------- /templates/_gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | .idea 4 | .classpath 5 | .project 6 | .gradle 7 | .settings 8 | node_modules 9 | src/main/resources/static 10 | src/main/resources/templates 11 | src/main/resources/bower_components 12 | -------------------------------------------------------------------------------- /templates/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= appName %>", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular-animate": "~1.4.4", 6 | "angular-bootstrap-confirm": "^2.3.0", 7 | "angular-bootstrap": "^1.1", 8 | "angular-cookies": "~1.4.4", 9 | "angular-google-maps": "2.3.2", 10 | "angular-highlightjs": "~0.6.0", 11 | "angular-messages": "~1.4.4", 12 | "angular-mocks": "~1.4.4", 13 | "angular-sanitize": "~1.4.4", 14 | "angular-ui-router": "~0.2.15", 15 | "angular-ui-tinymce": "~0.0.9", 16 | "angular-x2js": "https://github.com/janmichaelyu/angular-x2js.git", 17 | "angular": "~1.4.4", 18 | "bootstrap": "~3.3.5", 19 | "font-awesome": "~4.6.0", 20 | "highcharts": "^4.2", 21 | "highlightjs":"~8.7.0", 22 | "jquery": "~2.1.4", 23 | "lodash": "~3.10.1", 24 | "ml-highcharts-ng": "~0.0.11", 25 | "ml-search-ng": "~0.2.0", 26 | "ng-json-explorer": "f7236fa857", 27 | "ngtoast": "^2.0.0", 28 | "tinymce-dist": "4.3.12", 29 | "view-file-ng": "^0.1.1", 30 | "vkbeautify-wrapper": "https://github.com/vkiryukhin/vkBeautify.git", 31 | "vkbeautify": "*" 32 | }, 33 | "overrides": { 34 | "angular-highlightjs": { 35 | "dependencies": {"angular" : ">1.0.8", "highlightjs":"~8.7.0"} 36 | }, 37 | "highlightjs": { 38 | "main": [ 39 | "highlight.pack.js", 40 | "styles/github.css" 41 | ] 42 | }, 43 | "vkbeautify": { 44 | "main": [ 45 | "vkbeautify.js" 46 | ] 47 | } 48 | }, 49 | "devDependencies": { 50 | "angular-mocks": "~1.4.4", 51 | "bardjs": "~0.1.8", 52 | "sinon": "*" 53 | }, 54 | "private": true, 55 | "resolutions": { 56 | "angular": "~1.4.4", 57 | "angular-bootstrap": "^1.1", 58 | "highcharts": "^4.2", 59 | "ng-json-explorer": "f7236fa857" 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /templates/gradle.properties: -------------------------------------------------------------------------------- 1 | # Properties are also stored in src/main/resources/application.properties, where Spring Boot can read them in 2 | 3 | # Port for a REST API server for testing (optional); set to 0 if not needed 4 | mlTestRestPort=<%= appTestRestPort %> 5 | 6 | # For deployment using Cargo 7 | targetContainerId=tomcat8x 8 | targetPort=8080 9 | targetHost=localhost 10 | targetContext=<%= appName %> 11 | deployUser=admin 12 | deployPassword= 13 | -------------------------------------------------------------------------------- /templates/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /templates/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 10 12:48:29 EST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip 7 | -------------------------------------------------------------------------------- /templates/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /templates/mlcp/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=INFO, stdout 3 | 4 | # Direct log messages to stdout 5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 6 | log4j.appender.stdout.Target=System.out 7 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 9 | -------------------------------------------------------------------------------- /templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= appName %>", 3 | "version": "0.0.0", 4 | "engines": { 5 | "node": ">=0.8.0" 6 | }, 7 | "private": true, 8 | "devDependencies": { 9 | "del": "^1.2.0", 10 | "gulp": "^3.9.1", 11 | "gulp-cssnano": "^2.1.1", 12 | "gulp-imagemin": "^2.4.0", 13 | "gulp-inject": "^3.0.0", 14 | "gulp-less": "^1.2.3", 15 | "gulp-plumber": "^1.0.1", 16 | "gulp-regex-replace": "^0.2.3", 17 | "gulp-replace": "^0.6.1", 18 | "gulp-wiredep": "0.0.0", 19 | "run-sequence": "^1.1.5", 20 | "yargs": "^8.0.2" 21 | }, 22 | "dependencies": { 23 | "angular": "angular/bower-angular#1.5.3", 24 | "angular-animate": "angular/bower-angular-animate#1.5.3", 25 | "angular-bootstrap": "angular-ui/bootstrap-bower#1.3.3", 26 | "angular-bootstrap-confirm": "mattlewis92/angular-bootstrap-confirm#2.3.0", 27 | "angular-cookies": "angular/bower-angular-cookies#1.5.3", 28 | "angular-google-maps": "angular-ui/angular-google-maps#2.3.2", 29 | "angular-highlightjs": "pc035860/angular-highlightjs#v0.6.3", 30 | "angular-messages": "angular/bower-angular-messages#1.5.3", 31 | "angular-mocks": "angular/bower-angular-mocks#1.5.3", 32 | "angular-recursion": "marklagendijk/angular-recursion#1.0.5", 33 | "angular-sanitize": "angular/bower-angular-sanitize#1.5.3", 34 | "angular-simple-logger": "nmccready/angular-simple-logger#0.1.7", 35 | "angular-ui-router": "angular-ui/ui-router#0.2.15", 36 | "angular-ui-tinymce": "angular-ui/ui-tinymce#v0.0.9", 37 | "angular-x2js": "janmichaelyu/angular-x2js#0.2.0", 38 | "bootstrap": "twbs/bootstrap#3.3.7", 39 | "font-awesome": "FortAwesome/Font-Awesome#4.6.3", 40 | "highcharts": "highcharts/highcharts-dist#4.2.7", 41 | "highcharts-ng":"pablojim/highcharts-ng#0.0.13", 42 | "highlightjs": "components/highlightjs#8.7.0", 43 | "jquery": "jquery/jquery-dist#2.1.4", 44 | "lodash" : "lodash/lodash#3.10.1", 45 | "ml-common-ng": "joemfb/ml-common-ng#v1.0.2", 46 | "ml-highcharts-ng": "kghmanuel/ml-highcharts-ng#*", 47 | "ml-search-ng": "joemfb/ml-search-ng#v0.2.8", 48 | "ng-json-explorer": "Goldark/ng-json-explorer#f7236fa857", 49 | "ngtoast": "tameraydin/ngToast#2.0.0", 50 | "tinymce": "tinymce/tinymce-dist#4.5.8", 51 | "tinymce-dist": "tinymce/tinymce-dist#4.3.12", 52 | "videogular": "2fdevs/bower-videogular#v1.4.4", 53 | "videogular-controls": "2fdevs/bower-videogular-controls#v1.4.4", 54 | "videogular-themes-default": "2fdevs/bower-videogular-themes-default#v1.4.4", 55 | "view-file-ng": "grtjn/view-file-ng#0.1.4", 56 | "vkbeautify": "2Ring/vkBeautify#*", 57 | "vkbeautify-wrapper": "2Ring/vkBeautify#*", 58 | "x2js": "abdmob/x2js#v1.2.0" 59 | }, 60 | "overrides": { 61 | "ml-highcharts-ng": { 62 | "dependencies": { 63 | "angular": "^1.2", 64 | "angular-bootstrap": "^1.1", 65 | "highcharts": ">= 4.1.8", 66 | "highcharts-ng": "~0.0.12", 67 | "lodash": ">= 2.4.x", 68 | "ml-common-ng": ">= 0.0.6", 69 | "ml-search-ng": ">= 0.1.0" 70 | } 71 | }, 72 | "angular-highlightjs": { 73 | "dependencies": {"angular" : ">1.0.8", "highlightjs":"~8.7.0"} 74 | }, 75 | "highlightjs": { 76 | "main": [ 77 | "highlight.pack.js", 78 | "styles/github.css" 79 | ] 80 | }, 81 | "vkbeautify": { 82 | "main": [ 83 | "vkbeautify.js" 84 | ] 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /templates/src/main/java/org/example/App.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.boot.Banner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.builder.SpringApplicationBuilder; 7 | import org.springframework.boot.web.support.SpringBootServletInitializer; 8 | import org.springframework.scheduling.annotation.EnableScheduling; 9 | import org.springframework.web.WebApplicationInitializer; 10 | 11 | /** 12 | * Fires up Spring Boot. 13 | */ 14 | @SpringBootApplication 15 | @EnableScheduling 16 | public class App extends SpringBootServletInitializer implements WebApplicationInitializer { 17 | @Override 18 | protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { 19 | return configureApplication(builder); 20 | } 21 | 22 | public static void main(String[] args) { 23 | SpringApplication.run(App.class, args); 24 | } 25 | 26 | private static SpringApplicationBuilder configureApplication(SpringApplicationBuilder builder) { 27 | return builder.sources(App.class).bannerMode(Banner.Mode.OFF); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /templates/src/main/java/org/example/DevConfig.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.example.util.ModuleWatcher; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Profile; 8 | import org.springframework.scheduling.annotation.EnableScheduling; 9 | 10 | import com.marklogic.client.helper.DatabaseClientConfig; 11 | import com.marklogic.client.spring.DatabaseClientManager; 12 | 13 | /** 14 | * The beans in this configuration are only intended to be used in a development environment. 15 | */ 16 | @Configuration 17 | @Profile("dev") 18 | @EnableScheduling 19 | public class DevConfig { 20 | 21 | @Value("${mlAppName}") 22 | protected String mlAppName; 23 | 24 | @Value("${mlHost:localhost}") 25 | protected String mlHost; 26 | 27 | @Value("${mlRestPort}") 28 | protected Integer mlRestPort; 29 | 30 | @Value("${mlRestAdminUsername}") 31 | protected String mlRestAdminUsername; 32 | 33 | @Value("${mlRestAdminPassword}") 34 | protected String mlRestAdminPassword; 35 | 36 | @Bean 37 | public DatabaseClientConfig contentDatabaseClientConfig() { 38 | DatabaseClientConfig config = new DatabaseClientConfig(mlHost, mlRestPort, mlRestAdminUsername, 39 | mlRestAdminPassword); 40 | config.setDatabase(mlAppName + "-content"); 41 | return config; 42 | } 43 | 44 | @Bean 45 | public DatabaseClientManager contentDatabaseClientManager() { 46 | return new DatabaseClientManager(contentDatabaseClientConfig()); 47 | } 48 | 49 | @Bean 50 | public DatabaseClientManager modulesDatabaseClientManager() { 51 | DatabaseClientConfig config = new DatabaseClientConfig(mlHost, mlRestPort, mlRestAdminUsername, 52 | mlRestAdminPassword); 53 | config.setDatabase(mlAppName + "-modules"); 54 | return new DatabaseClientManager(config); 55 | } 56 | 57 | @Bean 58 | public ModuleWatcher moduleWatcher() { 59 | return new ModuleWatcher(contentDatabaseClientManager().getObject(), 60 | modulesDatabaseClientManager().getObject()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /templates/src/main/java/org/example/MimeMappingConfig.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | 4 | import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; 5 | import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; 6 | import org.springframework.boot.context.embedded.MimeMappings; 7 | 8 | import org.springframework.context.annotation.Configuration; 9 | /** 10 | * Configuration to map file extensions to mime type header in http responses 11 | */ 12 | @Configuration 13 | public class MimeMappingConfig implements EmbeddedServletContainerCustomizer { 14 | 15 | /** 16 | * Customizes the container configuration programmatically (replacement for web.xml) 17 | * 18 | * @return 19 | */ 20 | @Override 21 | public void customize(ConfigurableEmbeddedServletContainer container) { 22 | MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT); 23 | mappings.add("woff2", "font/woff2"); 24 | container.setMimeMappings(mappings); 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /templates/src/main/java/org/example/client/DigestRestClientSession.java: -------------------------------------------------------------------------------- 1 | package org.example.client; 2 | 3 | import org.apache.http.auth.Credentials; 4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 5 | import org.springframework.security.core.GrantedAuthority; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | import java.security.Principal; 9 | import java.util.Collection; 10 | 11 | /** 12 | * Downstream authentication token designed to also be a holder for the Digest authenticated upstream RestTemplate. 13 | * 14 | */ 15 | public class DigestRestClientSession extends UsernamePasswordAuthenticationToken implements Credentials { 16 | final private String userName; 17 | final private String password; 18 | final private Principal principal; 19 | private RestTemplate restTemplate; 20 | 21 | public DigestRestClientSession(final Object principal, Object credentials, 22 | Collection authorities) { 23 | super(principal, credentials, authorities); 24 | this.userName = (principal==null?"":(String)principal); 25 | this.password = (credentials==null?"":(String)credentials); 26 | this.principal = new Principal() { 27 | @Override 28 | public String getName() { 29 | return userName; 30 | } 31 | }; 32 | } 33 | 34 | public RestTemplate getRestTemplate() { 35 | return restTemplate; 36 | } 37 | 38 | public void setRestTemplate(RestTemplate restTemplate) { 39 | this.restTemplate = restTemplate; 40 | } 41 | 42 | 43 | @Override 44 | public Principal getUserPrincipal() { 45 | return this.principal; 46 | } 47 | 48 | @Override 49 | public String getPassword() { 50 | //needed to be kept to compute authentication headers 51 | return this.password; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /templates/src/main/java/org/example/client/DigestRestTemplateLoader.java: -------------------------------------------------------------------------------- 1 | package org.example.client; 2 | 3 | import com.google.common.cache.CacheLoader; 4 | import com.marklogic.spring.http.RestConfig; 5 | import org.apache.http.HttpHost; 6 | import org.apache.http.auth.AuthScope; 7 | import org.apache.http.client.AuthCache; 8 | import org.apache.http.client.CredentialsProvider; 9 | import org.apache.http.client.protocol.HttpClientContext; 10 | import org.apache.http.impl.auth.DigestScheme; 11 | import org.apache.http.impl.client.BasicAuthCache; 12 | import org.apache.http.impl.client.BasicCredentialsProvider; 13 | import org.apache.http.impl.client.CloseableHttpClient; 14 | import org.apache.http.impl.client.HttpClientBuilder; 15 | import org.apache.http.protocol.BasicHttpContext; 16 | import org.apache.http.protocol.HttpContext; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.http.HttpMethod; 19 | import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; 20 | import org.springframework.stereotype.Component; 21 | import org.springframework.web.client.RestTemplate; 22 | 23 | import java.net.URI; 24 | 25 | /** 26 | * Loads RestTemplate {@link org.springframework.web.client.RestTemplate} objects wired for Digest authentication. 27 | */ 28 | @Component 29 | public class DigestRestTemplateLoader extends CacheLoader { 30 | 31 | @Autowired 32 | private RestConfig restConfig; 33 | 34 | @Override 35 | public RestTemplate load(DigestRestClientSession clientSession) throws Exception { 36 | final CredentialsProvider provider = new BasicCredentialsProvider(); 37 | provider.setCredentials(AuthScope.ANY, clientSession); 38 | final CloseableHttpClient httpClient = 39 | HttpClientBuilder.create(). 40 | setDefaultCredentialsProvider(provider).useSystemProperties().build(); 41 | final HttpHost host = new HttpHost(restConfig.getHost(), restConfig.getRestPort(), restConfig.getScheme()); 42 | // Create AuthCache instance 43 | final AuthCache authCache = new BasicAuthCache(); 44 | // Generate DIGEST scheme object, initialize it and add it to the local digest cache 45 | final DigestScheme digestAuth = new DigestScheme(); 46 | digestAuth.overrideParamter("realm", "public"); 47 | authCache.put(host, digestAuth); 48 | 49 | // create a RestTemplate wired with a custom request factory using the above AuthCache with Digest Scheme 50 | RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient){ 51 | @Override 52 | protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) { 53 | // Add AuthCache to the execution context 54 | BasicHttpContext localcontext = new BasicHttpContext(); 55 | localcontext.setAttribute(HttpClientContext.AUTH_CACHE, authCache); 56 | return localcontext; 57 | } 58 | }); 59 | 60 | //link template to the session 61 | clientSession.setRestTemplate(restTemplate); 62 | 63 | return restTemplate; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /templates/src/main/java/org/example/util/ModuleWatcher.java: -------------------------------------------------------------------------------- 1 | package org.example.util; 2 | 3 | import java.io.File; 4 | 5 | import org.springframework.scheduling.annotation.Scheduled; 6 | 7 | import com.marklogic.client.DatabaseClient; 8 | import com.marklogic.client.helper.LoggingObject; 9 | import com.marklogic.client.modulesloader.ModulesFinder; 10 | import com.marklogic.client.modulesloader.impl.DefaultModulesFinder; 11 | import com.marklogic.client.modulesloader.impl.DefaultModulesLoader; 12 | import com.marklogic.client.modulesloader.impl.RestApiAssetLoader; 13 | 14 | /** 15 | * Loads new/modified modules from src/main/ml-modules. Only intended to run in a dev profile. It means you can run 16 | * "gradle bootRun", and the equivalent of "gradle mlWatch" will run as well. 17 | */ 18 | public class ModuleWatcher extends LoggingObject { 19 | 20 | private DatabaseClient contentDatabaseClient; 21 | private DatabaseClient modulesDatabaseClient; 22 | private DefaultModulesLoader loader; 23 | 24 | private String modulesPath = "src/main/ml-modules"; 25 | private ModulesFinder modulesFinder = new DefaultModulesFinder(); 26 | 27 | public ModuleWatcher(DatabaseClient contentDatabaseClient, DatabaseClient modulesDatabaseClient) { 28 | this.contentDatabaseClient = contentDatabaseClient; 29 | this.modulesDatabaseClient = modulesDatabaseClient; 30 | this.loader = new DefaultModulesLoader(); 31 | this.loader.setCatchExceptions(true); 32 | if (logger.isInfoEnabled()) { 33 | logger.info("Initialized"); 34 | } 35 | } 36 | 37 | /** 38 | * RestApiAssetLoader isn't thread-safe, so a new instance is created on each invocation. 39 | */ 40 | @Scheduled(fixedDelay = 2000) 41 | public void checkForModulesToLoad() { 42 | loader.setRestApiAssetLoader(new RestApiAssetLoader(modulesDatabaseClient)); 43 | loader.loadModules(new File(modulesPath), modulesFinder, contentDatabaseClient); 44 | } 45 | 46 | public void setModulesPath(String modulesPath) { 47 | this.modulesPath = modulesPath; 48 | } 49 | 50 | public void setModulesFinder(ModulesFinder modulesFinder) { 51 | this.modulesFinder = modulesFinder; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /templates/src/main/java/org/example/util/URIUtil.java: -------------------------------------------------------------------------------- 1 | package org.example.util; 2 | 3 | import com.marklogic.spring.http.RestConfig; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.io.UnsupportedEncodingException; 8 | import java.net.URI; 9 | import java.net.URLDecoder; 10 | 11 | /** 12 | * Utility class for URI related actions. 13 | */ 14 | @Component 15 | public class URIUtil { 16 | 17 | private static final boolean decodeQuerystring = true; 18 | private static final String encoding = "UTF-8"; 19 | 20 | @Autowired 21 | private RestConfig restConfig; 22 | 23 | public URI buildUri(String path, String queryString) { 24 | try { 25 | /** 26 | * Gotta decode this, the URI constructor will then encode it. The QS will often have encoded text on it 27 | * already, such as for a structured query for a /v1/search request. The text is then decoded here, and then 28 | * encoded by the URI class, which ensures that we don't double-encode the QS. 29 | */ 30 | if (isDecodeQuerystring()) { 31 | queryString = decode(queryString); 32 | } 33 | return new URI(restConfig.getScheme(), null, restConfig.getHost(), restConfig.getRestPort(), path, 34 | queryString, null); 35 | } catch (Exception ex) { 36 | throw new RuntimeException("Unable to build URI, cause: " + ex.getMessage(), ex); 37 | } 38 | } 39 | 40 | protected String decode(String queryString) { 41 | try { 42 | return queryString != null ? URLDecoder.decode(queryString, "UTF-8") : null; 43 | } catch (UnsupportedEncodingException ex) { 44 | throw new RuntimeException("Unable to decode queryString, cause: " + ex.getMessage(), ex); 45 | } 46 | } 47 | 48 | public static boolean isDecodeQuerystring() { 49 | return decodeQuerystring; 50 | } 51 | 52 | public static String getEncoding() { 53 | return encoding; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/README.md: -------------------------------------------------------------------------------- 1 | The rest-api.json file in the root of the ml-config directory is used for the initial call to 2 | [the Client REST API](http://docs.marklogic.com/REST/POST/v1/rest-apis) to create a REST API server. This API is 3 | separate from the Management REST API, and it only allows for a handful of configuration properties. But it's a very 4 | handy API to use, as it automatically creates content and modules databases to with the REST API server. 5 | 6 | You don't need to include a rest-api.json file in this directory; if you don't, some sensible defaults will be used 7 | by ml-gradle. 8 | 9 | Note that in order to "fully" configure the REST API server - e.g. modifying its authentication strategy or its 10 | rewriter - you will instead use ml-config/servers/rest-api-server.json, which is instead processed via the 11 | [endpoint for creating and updating servers](http://docs.marklogic.com/REST/PUT/manage/v2/servers/[id-or-name]/properties). 12 | 13 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 14 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/cpf/cpf-configs/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/databases/[id-or-name]/cpf-configs) for what a 2 | CPF configuration JSON/XML file can contain. 3 | 4 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 5 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/cpf/domains/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/databases/[id-or-name]/domains) for what a 2 | CPF domain JSON/XML file can contain. 3 | 4 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 5 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/cpf/pipelines/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/databases/[id-or-name]/pipelines) for what a 2 | CPF pipeline JSON/XML file can contain. 3 | 4 | Note that as of MarkLogic 8.0-3, the pipeline schema does not allow for a condition element to have a nested options 5 | element. This will be fixed in 8.0-4. In the meantime, the best way to work around this is to create your own 6 | condition module that does not require an options element - in effect, move the logic expressed by the options element 7 | into a custom condition. 8 | 9 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 10 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/databases/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/databases) for what a database JSON/XML file can 2 | contain. 3 | 4 | As shown in this directory, you can have multiple files for configuring your content database. See the build.gradle 5 | file to see how more-content-database-config.json is added to the list of content database config files. 6 | 7 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 8 | 9 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/databases/content-database.json: -------------------------------------------------------------------------------- 1 | { 2 | "database-name": "%%DATABASE%%", 3 | "collection-lexicon": true, 4 | "word-searches": true, 5 | "trailing-wildcard-searches": true, 6 | "uri-lexicon": true, 7 | "geospatial-element-pair-index": [ 8 | { 9 | "parent-namespace-uri": "", 10 | "parent-localname": "location", 11 | "latitude-namespace-uri": "", 12 | "latitude-localname": "latitude", 13 | "longitude-namespace-uri": "", 14 | "longitude-localname": "longitude", 15 | "coordinate-system": "wgs84", 16 | "range-value-positions": false 17 | } 18 | ], 19 | "element-word-query-through": 20 | [], 21 | "phrase-through": 22 | [], 23 | "phrase-around": 24 | [], 25 | "range-element-index": 26 | [], 27 | "range-path-index": [ 28 | { 29 | "scalar-type": "string", 30 | "collation": "http://marklogic.com/collation/codepoint", 31 | "path-expression": "docFormat", 32 | "range-value-positions": false, 33 | "invalid-values": "reject" 34 | }, 35 | { 36 | "scalar-type": "string", 37 | "collation": "http://marklogic.com/collation/codepoint", 38 | "path-expression": "eyeColor", 39 | "range-value-positions": false, 40 | "invalid-values": "reject" 41 | },{ 42 | "scalar-type": "string", 43 | "collation": "http://marklogic.com/collation/codepoint", 44 | "path-expression": "gender", 45 | "range-value-positions": false, 46 | "invalid-values": "reject" 47 | },{ 48 | "scalar-type": "unsignedInt", 49 | "path-expression": "age", 50 | "collation": "http://marklogic.com/collation/", 51 | "range-value-positions": false, 52 | "invalid-values": "reject" 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/databases/modules-database.json: -------------------------------------------------------------------------------- 1 | { 2 | "database-name": "%%MODULES_DATABASE%%", 3 | "stemmed-searches": "off", 4 | "fast-phrase-searches": false, 5 | "fast-case-sensitive-searches": false, 6 | "fast-diacritic-sensitive-searches": false, 7 | "fast-element-word-searches": false, 8 | "fast-element-phrase-searches": false, 9 | "maintain-last-modified": false, 10 | "geospatial-element-pair-index": [], 11 | "element-word-query-through": 12 | [], 13 | "phrase-through": 14 | [], 15 | "phrase-around": 16 | [], 17 | "range-element-index": 18 | [], 19 | "range-path-index": 20 | [] 21 | } 22 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/forests/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/forests) for what a forest JSON/XML file can 2 | contain. 3 | 4 | Defining a content-forest.json file is optional - without it, ml-gradle will still create "plain vanilla" forests 5 | for the main content database and the optional test content database. The DeployContentDatabasesCommand is configured 6 | to look for content-forest.json by default, and if it exists, that command will use that file for creating content 7 | forests. 8 | 9 | There currently isn't a command that will just iterate over every forest file in the forests directory and process it. 10 | Instead, similar to databases, you would write a new command that reads a specific file in the forests directory. 11 | 12 | If you do make such a command, consider extending DeployForestsCommand, which supports the tokens %%FOREST_NAME%%, 13 | %%FOREST_HOST%%, and %%FOREST_DATABASE%%. 14 | 15 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 16 | 17 | 18 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/groups/README.md: -------------------------------------------------------------------------------- 1 | 2 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 3 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/groups/default-group.xml: -------------------------------------------------------------------------------- 1 | 2 | Default 3 | 5 | 2048 6 | 7 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/rest-api.json: -------------------------------------------------------------------------------- 1 | { 2 | "rest-api": { 3 | "name": "%%NAME%%", 4 | "group": "%%GROUP%%", 5 | "database": "%%DATABASE%%", 6 | "modules-database": "%%MODULES_DATABASE%%", 7 | "port": "%%PORT%%", 8 | "xdbc-enabled": true, 9 | "forests-per-host": 3, 10 | "error-format": "json" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/security/amps/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/amps) for what an amp JSON/XML file can contain. 2 | 3 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 4 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/security/external-security/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/external-security) for what an external 2 | security JSON/XML file can contain. 3 | 4 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 5 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/security/privileges/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/privileges) for what a privilege JSON/XML file can contain. 2 | 3 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 4 | 5 | Note on how to avoid issues when deploying security assets [as noted here](https://github.com/marklogic-community/ml-app-deployer/issues/114). 6 | 7 | Create the privilege without any roles: 8 | 9 | `{ 10 | "privilege-name": "mltap-runner", 11 | "action": "http://github.com/jmakeig/mltap/runner", 12 | "kind": "execute", 13 | "role": [ ] 14 | } 15 | ` 16 | 17 | Add the privilege to the privilege array of the newly created role: 18 | 19 | ` 20 | { 21 | "role-name": "mltap-tester", 22 | "description": "", 23 | "privilege": [ 24 | { 25 | "action": "http://marklogic.com/xdmp/privileges/xdbc-eval", 26 | "kind": "execute", 27 | "privilege-name": "xdbc:eval" 28 | }, 29 | { 30 | "action": "http://github.com/jmakeig/mltap/runner", 31 | "kind": "execute", 32 | "privilege-name": "mltap-runner" 33 | } 34 | ] 35 | } 36 | ` 37 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/security/protected-collections/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/protected-collections) for what a protected 2 | collection JSON/XML file can contain. 3 | 4 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 5 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/security/roles/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/roles) for what a role JSON/XML file can contain. 2 | 3 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 4 | 5 | Note on how to avoid issues when deploying security assets [as noted here](https://github.com/marklogic-community/ml-app-deployer/issues/114). 6 | 7 | Create the privilege without any roles: 8 | 9 | `{ 10 | "privilege-name": "mltap-runner", 11 | "action": "http://github.com/jmakeig/mltap/runner", 12 | "kind": "execute", 13 | "role": [ ] 14 | } 15 | ` 16 | 17 | Add the privilege to the privilege array of the newly created role: 18 | 19 | ` 20 | { 21 | "role-name": "mltap-tester", 22 | "description": "", 23 | "privilege": [ 24 | { 25 | "action": "http://marklogic.com/xdmp/privileges/xdbc-eval", 26 | "kind": "execute", 27 | "privilege-name": "xdbc:eval" 28 | }, 29 | { 30 | "action": "http://github.com/jmakeig/mltap/runner", 31 | "kind": "execute", 32 | "privilege-name": "mltap-runner" 33 | } 34 | ] 35 | } 36 | ` 37 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/security/roles/sample-app-role.json: -------------------------------------------------------------------------------- 1 | { 2 | "role-name": "<%= appName %>-role", 3 | "description": "A role for users of the <%= appName %> application", 4 | "role": ["rest-reader", "rest-writer"] 5 | } 6 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/security/users/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/users) for what a user JSON/XML file can contain. 2 | 3 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 4 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/security/users/sample-project-default-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "user-name": "<%= appName %>-default-user", 3 | "description": "The default user for the <%= appName %> application", 4 | "password": "password", 5 | "role": ["<%= appName %>-role"] 6 | } 7 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/servers/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/servers) for what a server JSON/XML file can contain. 2 | 3 | ml-gradle will always look for a file named "rest-api-server.json" in this directory. If found, this file is used to 4 | update the REST API application whose name equals that of the "mlAppName" property. ml-gradle is really designed for 5 | applications based on the REST API, so it's likely that you'll want to use this file to configure the REST API server. 6 | Note that the endpoint for creating a REST API application - http://docs.marklogic.com/REST/POST/v1/rest-apis - is 7 | part of the Client REST API and only provides a few options for configuring the REST API server. You can override 8 | those options via the ml-config/rest-properties.json file. 9 | 10 | ml-gradle will process any other .json/.xml file found in this directory, creating/updating a server as necessary. 11 | 12 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 13 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/tasks/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/tasks) for what a scheduled task JSON/XML file can contain. 2 | 3 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 4 | -------------------------------------------------------------------------------- /templates/src/main/ml-config/view-schemas/README.md: -------------------------------------------------------------------------------- 1 | See [the MarkLogic docs](http://docs.marklogic.com/REST/POST/manage/v2/databases/[id-or-name]/view-schemas) for what a 2 | SQL view schema and view JSON/XML file can contain. 3 | 4 | For each view schema that ml-gradle processes in this directory, it will look for a directory with a name 5 | of "(view schema name)-views". If it finds such a directory, it will process each file within the directory 6 | as a SQL view. In this example, the name of the view schema is "main", and so the "main-views" directory 7 | contains the views to be created in association with this schema. 8 | 9 | See [more ml-config samples](https://github.com/marklogic-community/ml-gradle/tree/master/examples/sample-project/src/main/ml-config) from ml-gradle github project. 10 | -------------------------------------------------------------------------------- /templates/src/main/ml-content/CONTENTS-ARE-NOT-AUTO-DEPLOYED.md: -------------------------------------------------------------------------------- 1 | Readme info taken from [Migrating a Roxy project to ml gradle](https://github.com/marklogic-community/ml-gradle/wiki/Migrating-a-Roxy-project-to-ml-gradle) page. 2 | 3 | # Loading data 4 | 5 | ml-gradle doesn't have a pre-defined task for loading data from a specific directory like Roxy does - e.g. the "data" directory and "ml local deploy content". Instead, you can configure as many data-loading tasks as you want using [MLCP (Content Pump)](https://developer.marklogic.com/products/mlcp). Please see [this guide](https://github.com/marklogic-community/ml-gradle/wiki/Content-Pump-and-Gradle) for more information. 6 | 7 | The reason why ml-gradle doesn't have a pre-defined task for loading data is so that ml-gradle doesn't depend on a specific version of MLCP. Instead, you can use whatever version of MLCP you need via Gradle's normal dependency management. 8 | 9 | 10 | Tip: Check the deployContent task in build.gradle file. 11 | -------------------------------------------------------------------------------- /templates/src/main/ml-content/api/users/admin.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /templates/src/main/ml-content/api/users/sample-project-default-user.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /templates/src/main/ml-content/sample-data.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/ml-content/sample-data.zip -------------------------------------------------------------------------------- /templates/src/main/ml-modules/ext/corb/transform.xqy: -------------------------------------------------------------------------------- 1 | xquery version "1.0-ml"; 2 | 3 | declare variable $URI external; 4 | 5 | xdmp:log("Calling Corb transform on URI: " || $URI) 6 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/ext/corb/uris.xqy: -------------------------------------------------------------------------------- 1 | xquery version "1.0-ml"; 2 | 3 | (: Sample query that returns all URIs. :) 4 | 5 | let $uris := cts:uris((), (), cts:and-query(())) 6 | 7 | return (count($uris), $uris) -------------------------------------------------------------------------------- /templates/src/main/ml-modules/root/config/config.xqy: -------------------------------------------------------------------------------- 1 | (: 2 | Copyright 2012-2015 MarkLogic Corporation 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | :) 16 | xquery version "1.0-ml"; 17 | 18 | module namespace c = "http://marklogic.com/roxy/application-config"; 19 | 20 | (: configured at deploy time by Roxy deployer :) 21 | declare variable $c:app-name := "@ml.app-name"; 22 | 23 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/root/lib/utilities.xqy: -------------------------------------------------------------------------------- 1 | xquery version "1.0-ml"; 2 | 3 | module namespace util = "http://marklogic.com/utilities"; 4 | 5 | declare default function namespace "http://www.w3.org/2005/xpath-functions"; 6 | 7 | declare option xdmp:mapping "false"; (::) 8 | 9 | (: 10 | : Wrapper function for sending an email 11 | :) 12 | declare function util:send-notification( 13 | $recipient-name as xs:string, 14 | $recipient-email as xs:string, 15 | $subject as xs:string, 16 | $message as item() 17 | ) as empty-sequence() { 18 | xdmp:email( 19 | 22 | {$subject} 23 | 24 | 25 | MarkLogic Demo 26 | no-reply@demo.marklogic.com 27 | 28 | 29 | 30 | 31 | {$recipient-name} 32 | {$recipient-email} 33 | 34 | 35 | 36 | 37 | 38 | {$subject} 39 | 40 | {$message} 41 | 42 | 43 | 44 | ) 45 | }; 46 | 47 | declare function util:highlight($doc, $query) { 48 | cts:highlight($doc, $query, {$cts:text}) 49 | }; 50 | 51 | declare function util:is-binary($node) { 52 | $node instance of binary() 53 | or $node/node() instance of binary() 54 | }; 55 | 56 | declare function util:get-content-type($uri) { 57 | xdmp:uri-content-type($uri) 58 | }; 59 | 60 | declare function util:get-content-type($uri, $doc) { 61 | let $content-type := 62 | if (util:is-binary($doc)) then 63 | xdmp:document-properties($uri)//*:meta[@name = 'content-type']/(@content, .)[not(. = ('', 'text/plain'))] 64 | else () 65 | return 66 | ($content-type, xdmp:uri-content-type($uri))[1] 67 | }; 68 | 69 | declare function util:get-filename($uri) { 70 | xdmp:url-decode(tokenize($uri, '/')[last()]) 71 | }; 72 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/root/login-check.xqy: -------------------------------------------------------------------------------- 1 | let $username := xdmp:get-current-user() 2 | return $username 3 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/root/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | 4 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/services/extsimilar.xqy: -------------------------------------------------------------------------------- 1 | xquery version "1.0-ml"; 2 | 3 | module namespace ext = "http://marklogic.com/rest-api/resource/extsimilar"; 4 | 5 | declare default function namespace "http://www.w3.org/2005/xpath-functions"; 6 | 7 | declare namespace roxy = "http://marklogic.com/roxy"; 8 | 9 | (: 10 | : To add parameters to the functions, specify them in the params annotations. 11 | : Example 12 | : declare %roxy:params("uri=xs:string", "priority=xs:int") ext:get(...) 13 | : This means that the get function will take two parameters, a string and an int. 14 | :) 15 | 16 | (: 17 | :) 18 | declare 19 | %roxy:params("uri=xs:string", "limit=xs:int?") 20 | function ext:get( 21 | $context as map:map, 22 | $params as map:map 23 | ) as document-node()* 24 | { 25 | ext:post($context, $params, ()) 26 | }; 27 | 28 | (: 29 | :) 30 | declare 31 | %roxy:params("uri=xs:string", "limit=xs:int?") 32 | function ext:post( 33 | $context as map:map, 34 | $params as map:map, 35 | $input as document-node()* 36 | ) as document-node()* 37 | { 38 | let $output-types := map:put($context, "output-types", "application/json") 39 | let $uri := map:get($params, "uri") 40 | let $limit := xs:int((map:get($params, "limit"), '5')[1]) 41 | let $collections := xdmp:document-get-collections($uri) 42 | let $collection-q := if (fn:exists($collections)) then cts:collection-query($collections) else () 43 | let $content := 44 | if (exists($uri)) then 45 | json:to-array( 46 | cts:uris( 47 | (), 48 | ("limit="||$limit), 49 | cts:and-query(( 50 | cts:not-query(cts:document-query($uri)), 51 | $collection-q, 52 | cts:similar-query(doc($uri)) 53 | )) 54 | ) 55 | ) 56 | else json:array() 57 | let $response := json:object() 58 | let $_ := map:put($response, "similar", $content) 59 | return (xdmp:set-response-code(200, "OK"), document { xdmp:to-json($response) }) 60 | }; 61 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/services/extspell.xqy: -------------------------------------------------------------------------------- 1 | xquery version "1.0-ml"; 2 | 3 | module namespace ext = "http://marklogic.com/rest-api/resource/extspell"; 4 | 5 | import module namespace spell = "http://marklogic.com/xdmp/spell" at "/MarkLogic/spell.xqy"; 6 | 7 | declare default function namespace "http://www.w3.org/2005/xpath-functions"; 8 | 9 | declare namespace roxy = "http://marklogic.com/roxy"; 10 | 11 | declare variable $dictionary := "/dictionary-large.xml"; 12 | 13 | (: 14 | : To add parameters to the functions, specify them in the params annotations. 15 | : Example 16 | : declare %roxy:params("uri=xs:string", "priority=xs:int") ext:get(...) 17 | : This means that the get function will take two parameters, a string and an int. 18 | :) 19 | 20 | (: 21 | :) 22 | declare 23 | %roxy:params("pqtxt=xs:string", "limit=xs:int?") 24 | function ext:get( 25 | $context as map:map, 26 | $params as map:map 27 | ) as document-node()* 28 | { 29 | ext:post($context, $params, ()) 30 | }; 31 | 32 | (: 33 | :) 34 | declare 35 | %roxy:params("pqtxt=xs:string", "limit=xs:int?") 36 | function ext:post( 37 | $context as map:map, 38 | $params as map:map, 39 | $input as document-node()* 40 | ) as document-node()* 41 | { 42 | let $output-types := map:put($context,"output-types", "application/json") 43 | let $pqtxt := map:get($params, "pqtxt") 44 | let $limit := xs:int((map:get($params, "limit"), 10)[1]) 45 | let $content := 46 | if (exists($pqtxt)) then 47 | json:to-array( 48 | ( 49 | for $suggest in spell:suggest($dictionary, $pqtxt) 50 | where $suggest != $pqtxt and xdmp:estimate(cts:search(collection(), $suggest)) > 0 51 | return $suggest 52 | )[1 to $limit] 53 | ) 54 | else json:array() 55 | let $response := json:object() 56 | let $_ := map:put($response, "suggestions", $content) 57 | return (xdmp:set-response-code(200,"OK"), document { xdmp:to-json($response) }) 58 | }; 59 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/transforms/download.xsl: -------------------------------------------------------------------------------- 1 | 2 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | " 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/transforms/extractedToJson.sjs: -------------------------------------------------------------------------------- 1 | /* 2 | REST transform for converting XML content to JSON (for use with extract-document-data). Copied from: 3 | http://stackoverflow.com/questions/37986731/extract-document-data-comes-as-xml-string-element-in-json-output/37994680#37994680 4 | */ 5 | 6 | /* jshint node:true,esnext:true */ 7 | /* global xdmp */ 8 | 9 | var json = require('/MarkLogic/json/json.xqy'); 10 | var config = json.config('custom'); 11 | 12 | function extractedToJson(context, params, content) { 13 | 'use strict'; 14 | 15 | var response = content.toObject(); 16 | 17 | if (response.results) { 18 | response.results.map(function(result) { 19 | if (result.extracted && result.extracted.content) { 20 | result.extracted.content.map(function(content, index) { 21 | if (content.match(/^{$metadata}), 27 | $content 28 | ) 29 | }; -------------------------------------------------------------------------------- /templates/src/main/ml-modules/transforms/from-json.xsl: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/transforms/get-property.xsl: -------------------------------------------------------------------------------- 1 | 2 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/transforms/indent.xsl: -------------------------------------------------------------------------------- 1 | 2 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/transforms/sanitize.xsl: -------------------------------------------------------------------------------- 1 | 2 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /templates/src/main/ml-modules/transforms/to-json.xsl: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /templates/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # MarkLogic properties - these are loaded by Gradle as well to avoid duplication 2 | mlAppName=<%= appName %> 3 | mlHost=<%= appHost %> 4 | mlRestPort=<%= appRestPort %> 5 | 6 | # Credentials for deploying 7 | mlUsername=<%= appAdminUsername %> 8 | mlPassword=<%= appAdminPassword %> 9 | 10 | # Credentials for loading modules 11 | mlRestAdminUsername=<%= appAdminUsername %> 12 | mlRestAdminPassword=<%= appAdminPassword %> 13 | 14 | # Spring Boot property for the port that the app server runs on 15 | server.port=<%= appBootPort %> 16 | 17 | # By default, enable the dev profile 18 | spring.profiles.active=dev 19 | 20 | # Disable Thymeleaf caching 21 | spring.thymeleaf.cache=false 22 | 23 | # No need to restart when a web file is modified 24 | spring.devtools.restart.exclude=static/**,templates/** 25 | 26 | 27 | # For more config options refer to: 28 | # https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html 29 | 30 | # NOTE: The following server.***.*** config DOES NOT carry over when deployed to standalone application servers. 31 | 32 | # The cookie name is application-specific so it doesn't conflict with other Spring Boot-based apps 33 | server.session.cookie.name=<%= appName %>sessionid 34 | 35 | # Default the timeout to 10 hours (Spring Boot docs say this is in seconds, but it seems to be minutes instead) 36 | server.session.timeout=600 37 | 38 | # Response compression 39 | server.compression.enabled=true 40 | server.compression.min-response-size=128 41 | 42 | # Can cache web resources by configuring the following; ensure that devtools isn't included, which will default the 43 | # cache-period to 0, which is ideal for development, but not otherwise 44 | #spring.resources.cache-period=360 45 | #spring.resources.chain.enabled=true 46 | 47 | -------------------------------------------------------------------------------- /templates/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app', [ 5 | // routing 6 | 'app.route', 7 | 8 | // http interceptors 9 | 'app.error', 10 | 'app.login', 11 | 12 | // top-level state 13 | 'app.root' 14 | ]); 15 | 16 | }()); 17 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/create/create.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.create', [ 5 | // inject dependencies 6 | 'cb.x2js', 7 | 'ml.common', 8 | 'ngToast', 9 | 'ui.router', 10 | 11 | // html dependencies 12 | 'mwl.confirm', 13 | 'ngMessages', 14 | 'ui.bootstrap', 15 | 'ui.tinymce' 16 | ]); 17 | }()); 18 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/detail/detail.controller.js: -------------------------------------------------------------------------------- 1 | /* global vkbeautify */ 2 | (function () { 3 | 4 | 'use strict'; 5 | 6 | angular.module('app.detail') 7 | .config(["MLRestProvider", function (MLRestProvider) { 8 | // Make MLRestProvider target url start with the page's base href (proxy) 9 | MLRestProvider.setPrefix(angular.element(document.querySelector('base')).attr('href')+'v1'); 10 | }]) 11 | .controller('DetailCtrl', DetailCtrl); 12 | 13 | DetailCtrl.$inject = ['doc', '$stateParams', 'MLRest', 'ngToast', 14 | '$state', '$scope', 'x2js']; 15 | 16 | // TODO: inject vkbeautify 17 | function DetailCtrl(doc, $stateParams, MLRest, toast, $state, $scope, x2js) { 18 | var ctrl = this; 19 | 20 | var uri = $stateParams.uri; 21 | 22 | var contentType = doc.headers('content-type').split(/;/)[0]; 23 | var encodedUri = encodeURIComponent(uri); 24 | 25 | /* jscs: disable */ 26 | if (contentType.lastIndexOf('application/json', 0) === 0) { 27 | /*jshint camelcase: false */ 28 | ctrl.xml = vkbeautify.xml(x2js.json2xml_str( 29 | { xml: doc.data } 30 | )); 31 | ctrl.json = doc.data; 32 | ctrl.type = 'json'; 33 | } else if (contentType.lastIndexOf('application/xml', 0) === 0) { 34 | ctrl.xml = vkbeautify.xml(doc.data); 35 | /*jshint camelcase: false */ 36 | ctrl.json = x2js.xml_str2json(doc.data).xml; 37 | ctrl.type = 'xml'; 38 | /* jscs: enable */ 39 | } else if (contentType.lastIndexOf('text/plain', 0) === 0) { 40 | ctrl.xml = doc.data; 41 | ctrl.json = {'Document' : doc.data}; 42 | ctrl.type = 'text'; 43 | } else if (contentType.lastIndexOf('application', 0) === 0 ) { 44 | ctrl.xml = 'Binary object'; 45 | ctrl.json = {'Document type' : 'Binary object'}; 46 | ctrl.type = 'binary'; 47 | } else { 48 | ctrl.xml = 'Error occured determining document type.'; 49 | ctrl.json = {'Error' : 'Error occured determining document type.'}; 50 | } 51 | 52 | function deleteDocument() { 53 | MLRest.deleteDocument(uri).then(function(response) { 54 | // TODO: not reached with code coverage yet! 55 | 56 | // create a toast with settings: 57 | toast.create({ 58 | className: 'warning', 59 | content: 'Deleted ' + uri, 60 | dismissOnTimeout: true, 61 | timeout: 2000, 62 | onDismiss: function () { 63 | //redirect to search page 64 | $state.go('root.search'); 65 | } 66 | }); 67 | }, function(response) { 68 | toast.danger(response.data); 69 | }); 70 | } 71 | 72 | angular.extend(ctrl, { 73 | doc : doc.data, 74 | uri : uri, 75 | contentType: contentType, 76 | fileName: uri.split('/').pop(), 77 | viewUri: 'v1/documents?uri=' + encodedUri + '&format=binary&transform=sanitize', 78 | downloadUri: 'v1/documents?uri=' + encodedUri + '&format=binary&transform=download', 79 | delete: deleteDocument 80 | }); 81 | } 82 | }()); 83 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/detail/detail.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: DetailCtrl', function () { 6 | 7 | var controller1, controller2, controller3, controller4, controller5; 8 | var xmldoc, jsondoc, txtdoc, bindoc, otherdoc; 9 | 10 | beforeEach(function() { 11 | bard.appModule('app.detail'); 12 | bard.inject('$controller', '$rootScope', 'MLRest', '$q'); 13 | 14 | bard.mockService(MLRest, { 15 | deleteDocument: function() { 16 | return $q.when(arguments[0].uri); 17 | } 18 | }); 19 | }); 20 | 21 | beforeEach(function () { 22 | // xml 23 | xmldoc = { 24 | headers: function() { return 'application/xml'; }, 25 | data: 'hi' 26 | }; 27 | controller1 = $controller('DetailCtrl', { 28 | doc: xmldoc, 29 | $scope: $rootScope.$new(), 30 | $stateParams: { uri: '/doc.xml' } 31 | }); 32 | 33 | // json 34 | jsondoc = { 35 | headers: function() { return 'application/json'; }, 36 | data: { 37 | name: 'hi' 38 | } 39 | }; 40 | controller2 = $controller('DetailCtrl', { 41 | doc: jsondoc, 42 | $scope: $rootScope.$new(), 43 | $stateParams: { uri: '/doc.json' } 44 | }); 45 | 46 | // txt 47 | txtdoc = { 48 | headers: function() { return 'text/plain'; }, 49 | data: 'hi' 50 | }; 51 | controller3 = $controller('DetailCtrl', { 52 | doc: txtdoc, 53 | $scope: $rootScope.$new(), 54 | $stateParams: { uri: '/doc.txt' } 55 | }); 56 | 57 | // bin 58 | bindoc = { 59 | headers: function() { return 'application/pdf'; }, 60 | data: 'xxxx' 61 | }; 62 | controller4 = $controller('DetailCtrl', { 63 | doc: bindoc, 64 | $scope: $rootScope.$new(), 65 | $stateParams: { uri: '/doc.pdf' } 66 | }); 67 | 68 | // unknown 69 | otherdoc = { 70 | headers: function() { return 'unknown'; }, 71 | data: 'xxxx' 72 | }; 73 | controller5 = $controller('DetailCtrl', { 74 | doc: otherdoc, 75 | $scope: $rootScope.$new(), 76 | $stateParams: { uri: '/doc.other' } 77 | }); 78 | 79 | $rootScope.$apply(); 80 | }); 81 | 82 | it('should be created successfully', function () { 83 | expect(controller1).to.be.defined; 84 | expect(controller2).to.be.defined; 85 | expect(controller3).to.be.defined; 86 | expect(controller4).to.be.defined; 87 | expect(controller5).to.be.defined; 88 | }); 89 | 90 | it('should have the doc data we gave it', function() { 91 | expect(controller1.doc).to.eq(xmldoc.data); 92 | expect(controller2.doc).to.eq(jsondoc.data); 93 | expect(controller3.doc).to.eq(txtdoc.data); 94 | expect(controller4.doc).to.eq(bindoc.data); 95 | expect(controller5.doc).to.eq(otherdoc.data); 96 | }); 97 | 98 | it('should delete docs', function() { 99 | controller1.delete(); 100 | controller2.delete(); 101 | controller3.delete(); 102 | controller4.delete(); 103 | controller5.delete(); 104 | // no expects, just for code coverage 105 | // TODO: then branch of deleteDocument not reached according code coverage?? 106 | }); 107 | 108 | }); 109 | }()); 110 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/detail/detail.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 25 | 28 | 29 |
30 |
31 |
32 | 33 | 36 | 47 |
48 |
49 | 50 |
51 |
Similar
52 |
53 | 54 |
55 |
56 |
57 |
58 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/detail/detail.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.detail', [ 5 | // inject dependencies 6 | 'cb.x2js', 7 | 'ml.common', // Using MLRest for delete 8 | 'ngToast', // Showing toast on delete 9 | 'ui.router', 10 | 11 | // html dependencies 12 | 'app.similar', 13 | 'ngJsonExplorer', 14 | 'mwl.confirm', // for delete confirmation popups 15 | 'ui.bootstrap', 16 | 'view.file' 17 | ]); 18 | }()); 19 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/detail/ml-similar.directive.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | 'use strict'; 4 | 5 | angular.module('app.similar') 6 | .directive('mlSimilar', mlSimilar); 7 | 8 | mlSimilar.$inject = ['MLRest']; 9 | 10 | function mlSimilar(mlRest) { 11 | return { 12 | restrict: 'E', 13 | templateUrl: 'app/detail/ml-similar.html', 14 | scope: { uri: '@', limit: '@?' }, 15 | link: function($scope) { 16 | $scope._limit = toInt($scope.limit, 10); 17 | $scope.similar = []; 18 | mlRest.extension('extsimilar', 19 | { 20 | method: 'GET', 21 | params: { 22 | 'rs:uri': $scope.uri, 23 | 'rs:limit': $scope._limit 24 | } 25 | }) 26 | .then(function(response) { 27 | var similar = (response && response.data && response.data.similar) || []; 28 | $scope.similar = angular.isArray(similar) ? similar : [similar]; 29 | if ($scope.similar.length > $scope._limit) { 30 | $scope.similar.length = $scope._limit; 31 | } 32 | }); 33 | } 34 | }; 35 | } 36 | 37 | function toInt(str, def) { 38 | if (str) { 39 | var i = parseInt('' + str); 40 | return (!isNaN(i) && (i > 0)) ? i : def; 41 | } else { 42 | return def; 43 | } 44 | } 45 | 46 | }()); 47 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/detail/ml-similar.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 8 |
-------------------------------------------------------------------------------- /templates/src/main/webapp/app/detail/ml-similar.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.similar', [ 5 | // inject dependencies 6 | 'ml.common', 7 | 8 | // html dependencies 9 | 'ui.router' 10 | ]); 11 | 12 | }()); 13 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/error/error.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.error', [ 5 | // inject dependencies 6 | 'app.messageBoard', 7 | 'ngToast' 8 | ]); 9 | }()); 10 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/error/errorInterceptor.service.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.error') 5 | .factory('errorInterceptor', ErrorInterceptor) 6 | .config(['$httpProvider', function($httpProvider) { 7 | $httpProvider.interceptors.push('errorInterceptor'); 8 | }]); 9 | 10 | ErrorInterceptor.$inject = ['$q', '$injector', 'ngToast']; 11 | 12 | function ErrorInterceptor($q, $injector, toast) { 13 | var errorInterceptor = { 14 | responseError: function(rejection) { 15 | var msg; 16 | var toastMsg; 17 | if (rejection.data && rejection.data.errorResponse) { 18 | msg = { 19 | title: rejection.data.errorResponse.status, 20 | body: rejection.data.errorResponse.message 21 | }; 22 | } else { 23 | msg = { 24 | title: (rejection.status === 401) ? 'Unauthorised' : ( 25 | (rejection.status >= 400 && rejection.status < 500) ? 'Bad Request' : 26 | 'Internal Server Error' 27 | ), 28 | body: '' 29 | }; 30 | } 31 | toastMsg = '' + msg.title + '

' + msg.body + '

'; 32 | toast.create({ 33 | className: 'danger', 34 | content: toastMsg, 35 | dismissOnTimeout: true, 36 | timeout: 2000, 37 | onDismiss: function () { 38 | } 39 | }); 40 | if (rejection.status >= 400 && rejection.status !== 401) { 41 | var messageBoardService = $injector.get('messageBoardService'); 42 | messageBoardService.message(msg); 43 | } 44 | return $q.reject(rejection); 45 | } 46 | }; 47 | return errorInterceptor; 48 | } 49 | }()); 50 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/error/errorInterceptor.service.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Service: errorInterceptor', function () { 6 | 7 | beforeEach(function() { 8 | bard.appModule('app.error'); 9 | bard.inject('$http'); 10 | }); 11 | 12 | it('should be created successfully', function () { 13 | // no expects, just for code coverage 14 | }); 15 | 16 | }); 17 | }()); 18 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/landing/landing.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.landing') 5 | .controller('LandingCtrl', LandingCtrl); 6 | 7 | LandingCtrl.$inject = ['$scope']; 8 | 9 | function LandingCtrl($scope) { 10 | var ctrl = this; 11 | 12 | angular.extend(ctrl, { 13 | }); 14 | } 15 | }()); 16 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/landing/landing.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: LandingCtrl', function () { 6 | 7 | var controller; 8 | 9 | beforeEach(function() { 10 | bard.appModule('app.landing'); 11 | bard.inject('$controller', '$rootScope'); 12 | }); 13 | 14 | beforeEach(function () { 15 | controller = $controller('LandingCtrl', { 16 | $scope: $rootScope.$new() 17 | }); 18 | $rootScope.$apply(); 19 | }); 20 | 21 | it('should be created successfully', function () { 22 | expect(controller).to.be.defined; 23 | }); 24 | 25 | }); 26 | }()); 27 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/landing/landing.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.landing', [ 5 | // inject dependencies 6 | 'ml.search', 7 | 'app.user', 8 | 9 | // html dependencies 10 | 'ui.bootstrap' 11 | ]); 12 | }()); 13 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/login/login-full.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/login/login-modal.html: -------------------------------------------------------------------------------- 1 |
2 | 9 | 12 |
13 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/login/login.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.login') 5 | .controller('LoginCtrl', LoginCtrl) 6 | .controller('LoginFullCtrl', LoginFullCtrl); 7 | 8 | LoginCtrl.$inject = ['$scope', 'loginService']; 9 | function LoginCtrl($scope, loginService) { 10 | var ctrl = this; 11 | angular.extend(ctrl, { 12 | username: null, 13 | password: null, 14 | login: login, 15 | loginService: loginService, 16 | logout: logout, 17 | cancel: cancel 18 | }); 19 | 20 | function login() { 21 | loginService.login(ctrl.username, ctrl.password).then(function(user) { 22 | callback(user); 23 | }); 24 | } 25 | 26 | function logout() { 27 | loginService.logout().then(function() { 28 | callback(); 29 | }); 30 | } 31 | 32 | function cancel() { 33 | callback(); 34 | } 35 | 36 | function callback(user) { 37 | if ($scope.callback && !loginService.loginError()) { 38 | $scope.callback({user: user}); 39 | } 40 | } 41 | } 42 | 43 | LoginFullCtrl.$inject = ['$state', '$stateParams']; 44 | function LoginFullCtrl($state, $stateParams) { 45 | var ctrl = this; 46 | angular.extend(ctrl, { 47 | showCancel: $stateParams.state !== 'root.landing', 48 | toState: toState 49 | }); 50 | 51 | function toState(user) { 52 | if (user) { 53 | var params = null; 54 | if ($stateParams.params && /^\{.*\}$/.test($stateParams.params)) { 55 | params = JSON.parse($stateParams.params); 56 | } 57 | $state.go($stateParams.state || 'root.landing', params); 58 | } else { 59 | $state.go('root.landing'); 60 | } 61 | } 62 | } 63 | 64 | }()); 65 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/login/login.directive.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | 'use strict'; 4 | 5 | angular.module('app.login') 6 | .directive('login', LoginDirective); 7 | 8 | function LoginDirective() { 9 | return { 10 | restrict: 'EA', 11 | controller: 'LoginCtrl', 12 | controllerAs: '$ctrl', 13 | replace: true, 14 | scope: { 15 | showCancel: '=', 16 | mode: '@', 17 | callback: '&' 18 | }, 19 | templateUrl: 'app/login/login.html' 20 | }; 21 | } 22 | 23 | }()); 24 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/login/login.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Username and/or Password Incorrect
4 |
5 | 6 |
7 |
8 | 9 |
10 | 11 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/login/login.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.login', [ 5 | // inject dependencies 6 | 'app.login', // for loginInterceptor 7 | 'app.messageBoard', 8 | 'app.user', // for loginInterceptor 9 | 'ml.common', 10 | 'ui.bootstrap', 11 | 'ui.router' 12 | ]); 13 | }()); 14 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/login/loginInterceptor.service.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.login') 5 | .factory('authInterceptor', AuthInterceptor) 6 | .config(['$httpProvider', function($httpProvider) { 7 | $httpProvider.interceptors.push('authInterceptor'); 8 | }]); 9 | 10 | AuthInterceptor.$inject = ['$q', '$rootScope', '$injector']; 11 | function AuthInterceptor($q, $rootScope, $injector) { 12 | var waitingForLoginDefer; 13 | var waitingLineForLoginDefer = 0; 14 | function reduceWaitingLine() { 15 | waitingLineForLoginDefer--; 16 | if (waitingLineForLoginDefer <= 0) { 17 | waitingForLoginDefer = null; 18 | } 19 | } 20 | $rootScope.$on('$stateChangeSuccess', function() { 21 | waitingLineForLoginDefer = 0; 22 | waitingForLoginDefer = null; 23 | }); 24 | 25 | var authInterceptor = { 26 | request: function(config) { 27 | if (/\/v1\//.test(config.url) && waitingForLoginDefer) { 28 | // hold off on additional REST API calls until login is resolved 29 | waitingLineForLoginDefer++; 30 | return waitingForLoginDefer.promise.then(function() { 31 | reduceWaitingLine(); 32 | return config; 33 | }, 34 | function() { 35 | reduceWaitingLine(); 36 | }); 37 | } 38 | return config; 39 | }, 40 | responseError: function(rejection) { 41 | // Not logged in or session has expired 42 | var userService = $injector.get('userService'); 43 | if (rejection.status === 419 || rejection.status === 401 && 44 | !waitingForLoginDefer && !userService.currentUser() && 45 | !/\/api\/user\/login/.test(rejection.config.url)) { 46 | var loginService = $injector.get('loginService'); 47 | var $http = $injector.get('$http'); 48 | waitingForLoginDefer = $q.defer(); 49 | waitingLineForLoginDefer = 1; 50 | 51 | // We use login method that logs the user in using the current credentials and 52 | // returns a promise 53 | loginService.loginPrompt().then( 54 | waitingForLoginDefer.resolve, waitingForLoginDefer.reject); 55 | // When the session recovered, make the same backend call again and chain the request 56 | return waitingForLoginDefer.promise.then(function() { 57 | reduceWaitingLine(); 58 | return $http(rejection.config); 59 | }, 60 | function() { 61 | reduceWaitingLine(); 62 | }); 63 | } 64 | return $q.reject(rejection); 65 | } 66 | }; 67 | return authInterceptor; 68 | } 69 | }()); 70 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/message-board/message-board-dir.html: -------------------------------------------------------------------------------- 1 |
2 |

{{msg.title || msg}}

3 |

{{msg.body}}

4 |
5 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/message-board/message-board.directive.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | 'use strict'; 4 | 5 | angular.module('app.messageBoard') 6 | .directive('messageBoard', MessageBoardDirective) 7 | .controller('MessageBoardController', MessageBoardController); 8 | 9 | function MessageBoardDirective() { 10 | return { 11 | restrict: 'E', 12 | controller: 'MessageBoardController', 13 | controllerAs: '$ctrl', 14 | replace: true, 15 | scope: { 16 | msg: '=' 17 | }, 18 | templateUrl: 'app/message-board/message-board.html' 19 | }; 20 | } 21 | 22 | MessageBoardController.$inject = ['$scope']; 23 | 24 | function MessageBoardController($scope) { 25 | var ctrl = this; 26 | angular.extend(ctrl, { 27 | isObject: isObject, 28 | collapseRaw: collapseRaw, 29 | expandRaw: expandRaw 30 | }); 31 | 32 | function isObject(item) { 33 | return _.isObject(item); 34 | } 35 | 36 | function collapseRaw() { 37 | ctrl.showRaw = false; 38 | } 39 | 40 | function expandRaw() { 41 | ctrl.showRaw = true; 42 | } 43 | } 44 | 45 | }()); 46 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/message-board/message-board.html: -------------------------------------------------------------------------------- 1 |
2 |

{{msg.title || msg}}

3 |

{{msg.body}}

4 |
5 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/message-board/message-board.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.messageBoard', [ 5 | // dependencies 6 | 'ui.router' // for $stateChangeSuccess event 7 | ]); 8 | }()); 9 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/message-board/message-board.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.messageBoard') 5 | .factory('messageBoardService', MessageBoardService); 6 | 7 | MessageBoardService.$inject = ['$rootScope']; 8 | function MessageBoardService($rootScope) { 9 | var _message = null; 10 | 11 | function message(msg) { 12 | if (msg === undefined) { 13 | return _message; 14 | } 15 | _message = msg; 16 | if (message) { 17 | $rootScope.$broadcast('message-board:set', _message); 18 | } else { 19 | $rootScope.$broadcast('message-board:cleared'); 20 | } 21 | } 22 | 23 | $rootScope.$on('$stateChangeSuccess', function() { 24 | message(null); 25 | }); 26 | 27 | return { 28 | message: message 29 | }; 30 | } 31 | }()); 32 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/root/root.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.root') 5 | .controller('RootCtrl', RootCtrl); 6 | 7 | RootCtrl.$inject = ['messageBoardService', 'userService', '$scope', 8 | '$state', 'appConfig']; 9 | 10 | function RootCtrl(messageBoardService, userService, $scope, 11 | $state, appConfig) { 12 | 13 | var rootCtrl = this; 14 | rootCtrl.currentYear = new Date().getUTCFullYear(); 15 | rootCtrl.messageBoardService = messageBoardService; 16 | angular.extend(rootCtrl, appConfig); 17 | 18 | $scope.$watch(userService.currentUser, function(newValue) { 19 | rootCtrl.currentUser = newValue; 20 | }); 21 | 22 | $scope.$watch(function() { 23 | return $state.current.name; 24 | }, function(newValue) { 25 | rootCtrl.currentState = newValue; 26 | }); 27 | } 28 | }()); 29 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/root/root.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: RootCtrl', function () { 6 | 7 | var controller; 8 | 9 | beforeEach(function() { 10 | bard.appModule('app.root'); 11 | bard.inject('$controller', '$rootScope'); 12 | 13 | var msg = {}; 14 | var user = { name: 'Guest' }; 15 | 16 | controller = $controller('RootCtrl', { 17 | $scope: $rootScope.$new(), 18 | messageBoardService: { 19 | message: function() { 20 | return msg; 21 | } 22 | }, 23 | userService: { 24 | currentUser: function() { 25 | return user; 26 | } 27 | }, 28 | $state: { 29 | get: function() {}, 30 | current: { 31 | name: 'root' 32 | } 33 | } 34 | }); 35 | 36 | $rootScope.$apply(); 37 | }); 38 | 39 | it('should be created successfully', function () { 40 | expect(controller).to.be.defined; 41 | }); 42 | 43 | }); 44 | }()); 45 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/root/root.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 23 | 24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 | 51 |
52 |
53 |
54 |
55 | 63 |
64 |
65 | 70 |
71 |
72 |
-------------------------------------------------------------------------------- /templates/src/main/webapp/app/root/root.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.root', [ 5 | // inject dependencies 6 | 'app.login', 7 | 'app.messageBoard', 8 | 'app.route', 9 | 'app.user', 10 | 'ui.router', 11 | 12 | // child states 13 | 'app.create', 14 | 'app.detail', 15 | 'app.landing', 16 | 'app.search', 17 | 'app.user' 18 | ]); 19 | }()); 20 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/route/route.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.route', [ 5 | // inject dependencies 6 | 'app.user', 7 | 'ml.common', 8 | 'ui.router' 9 | ]); 10 | }()); 11 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/ml-snippet.directive.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | 'use strict'; 4 | 5 | angular.module('app.snippet') 6 | .directive('mlSnippet', SnippetDirective) 7 | .controller('SnippetCtrl', SnippetCtrl); 8 | 9 | function SnippetDirective() { 10 | return { 11 | restrict: 'E', 12 | controller: 'SnippetCtrl', 13 | controllerAs: '$ctrl', 14 | replace: true, 15 | scope: { 16 | setSnippet: '&' 17 | }, 18 | templateUrl: 'app/search/ml-snippet.html' 19 | }; 20 | } 21 | 22 | SnippetCtrl.$inject = ['$scope']; 23 | 24 | function SnippetCtrl($scope) { 25 | $scope.snippets = ['detailed', 'compact']; 26 | 27 | var ctrl = this; 28 | angular.extend(ctrl, { 29 | setSnippetType: setSnippetType 30 | }); 31 | 32 | function setSnippetType(type) { 33 | $scope.snippetType = type; 34 | $scope.setSnippet({type: type}); 35 | } 36 | } 37 | 38 | }()); 39 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/ml-snippet.directive.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Directive: ml-snippet', function () { 6 | 7 | var elem, controller, snippet; 8 | 9 | beforeEach(function() { 10 | bard.appModule('app.snippet'); 11 | bard.inject('$compile', '$rootScope', '$templateCache', '$controller'); 12 | 13 | $templateCache.put( 'app/search/ml-snippet.html', 14 | '
' 15 | ); 16 | }); 17 | 18 | beforeEach(function() { 19 | var $scope = $rootScope.$new(); 20 | $scope.setSnippet = function(type) { 21 | snippet = type.type; // note: test framework doesn't seem to unwrap arguments in directive callbacks 22 | }; 23 | elem = angular.element(''); 24 | $compile(elem)($scope); 25 | $scope.$digest(); 26 | 27 | controller = $controller('SnippetCtrl', { $scope: $scope }); 28 | 29 | // flush promises 30 | $rootScope.$apply(); 31 | }); 32 | 33 | it('should compile', function() { 34 | expect(elem.hasClass('ml-snippet')).to.eq(true); 35 | }); 36 | 37 | it('should set snippet', function() { 38 | var type = 'xxx'; 39 | controller.setSnippetType(type); 40 | expect(snippet).to.eq(type); 41 | }); 42 | 43 | }); 44 | })(); 45 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/ml-snippet.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | 10 |
11 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/ml-snippet.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.snippet', [ 5 | // html dependencies 6 | 'ui.bootstrap' 7 | ]); 8 | }()); 9 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/search-results.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {{ result.label || result.uri }} 4 |

5 |
6 |
7 | 8 | {{ text.highlight || text }} 9 | 10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/search.controller.js: -------------------------------------------------------------------------------- 1 | /* global MLSearchController */ 2 | (function () { 3 | 'use strict'; 4 | 5 | angular.module('app.search') 6 | .controller('SearchCtrl', SearchCtrl); 7 | 8 | SearchCtrl.$inject = ['$scope', '$location', 'MLSearchFactory']; 9 | 10 | // inherit from MLSearchController 11 | var superCtrl = MLSearchController.prototype; 12 | SearchCtrl.prototype = Object.create(superCtrl); 13 | 14 | function SearchCtrl($scope, $location, searchFactory) { 15 | var ctrl = this; 16 | 17 | superCtrl.constructor.call(ctrl, $scope, $location, searchFactory.newContext()); 18 | 19 | ctrl.init(); 20 | 21 | ctrl.setSnippet = function(type) { 22 | ctrl.mlSearch.setSnippet(type); 23 | ctrl.search(); 24 | }; 25 | } 26 | }()); 27 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/search.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: SearchCtrl', function () { 6 | 7 | var controller; 8 | 9 | var results = [ 10 | { 11 | uri: 'abc' 12 | }, 13 | { 14 | uri: 'def' 15 | } 16 | ]; 17 | 18 | beforeEach(function() { 19 | bard.appModule('app.search'); 20 | bard.inject('$controller', '$q', '$rootScope', '$location', 21 | 'MLSearchFactory', 'MLRest'); 22 | 23 | bard.mockService(MLRest, { 24 | search: $q.when({ 25 | data: { 26 | results: results 27 | } 28 | }) 29 | }); 30 | 31 | }); 32 | 33 | beforeEach(function () { 34 | controller = $controller('SearchCtrl', { $scope: $rootScope.$new() }); 35 | $rootScope.$apply(); 36 | }); 37 | 38 | it('should be created successfully', function () { 39 | expect(controller).to.be.defined; 40 | }); 41 | 42 | // TODO: needs to run on login-success event.. 43 | // it('should update the current user if it changes', function() { 44 | // expect(controller.currentUser).to.not.be.defined; 45 | // }); 46 | 47 | it('should run an initial search', function() { 48 | expect(controller.response.results).to.eq(results); 49 | }); 50 | 51 | it('should run a search', function() { 52 | controller.response = null; 53 | controller.search('stuff'); 54 | $rootScope.$apply(); 55 | expect(controller.response.results).to.eq(results); 56 | }); 57 | 58 | it('should search on snippet change', function() { 59 | controller.response = null; 60 | controller.setSnippet('my-snippet'); 61 | $rootScope.$apply(); 62 | expect(controller.response.results).to.eq(results); 63 | }); 64 | }); 65 | }()); 66 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/search.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | 7 | 25 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/search/search.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.search', [ 5 | // inject dependencies 6 | 'ml.search', 7 | 8 | // html dependencies 9 | 'app.snippet', 10 | 'ml.search.tpls', 11 | 'ui.bootstrap', 12 | 'ui.router' 13 | ]); 14 | }()); 15 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/upload/upload.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
Upload files
6 |
7 |

8 | You can ingest new content into MarkLogic via Content Pump 10 | by dropping/selecting a file below and then optionally filling out any 11 | of the import options below the file selector. 12 |

13 | 14 |

Drop files here or click to select files.

15 | (Files will be uploaded automatically) 16 |
17 |
18 |
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/upload/uploader.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | 'use strict'; 4 | 5 | angular.module('app.uploader') 6 | .controller('uploaderCtrl', UploaderCtrl); 7 | 8 | UploaderCtrl.$inject = []; 9 | function UploaderCtrl() { 10 | var ctrl = this; 11 | ctrl.mlcp = { 12 | output_permissions: 'rest-reader,read,rest-writer,update' 13 | }; 14 | } 15 | 16 | })(); 17 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/upload/uploader.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.uploader', []); 5 | 6 | }()); 7 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/ml-user.directive.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | 'use strict'; 4 | 5 | angular.module('app.user') 6 | .directive('mlUser', UserDirective) 7 | .controller('UserCtrl', UserCtrl); 8 | 9 | function UserDirective() { 10 | return { 11 | restrict: 'EA', 12 | controller: 'UserCtrl', 13 | controllerAs: '$ctrl', 14 | replace: true, 15 | scope: { 16 | showCancel: '=', 17 | mode: '@', 18 | callback: '&' 19 | }, 20 | templateUrl: 'app/user/ml-user.html' 21 | }; 22 | } 23 | 24 | UserCtrl.$inject = ['$scope', 'userService', 'loginService']; 25 | 26 | function UserCtrl($scope, userService, loginService) { 27 | var ctrl = this; 28 | angular.extend(ctrl, { 29 | username: null, 30 | password: null, 31 | loginService: loginService 32 | }); 33 | $scope.$watch(userService.currentUser, function(newValue) { 34 | $scope.currentUser = newValue; 35 | }); 36 | } 37 | 38 | }()); 39 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/ml-user.directive.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Directive: ml-user', function () { 6 | 7 | var elem, controller; 8 | 9 | beforeEach(function() { 10 | bard.appModule('app.user'); 11 | bard.inject('$compile', '$rootScope', '$templateCache', '$controller'); 12 | 13 | $templateCache.put( 'app/user/ml-user.html', 14 | '
' 15 | ); 16 | }); 17 | 18 | beforeEach(function() { 19 | var $scope = $rootScope.$new(); 20 | elem = angular.element(''); 21 | $compile(elem)($scope); 22 | $scope.$digest(); 23 | 24 | var user = { name: 'Guest' }; 25 | 26 | controller = $controller('UserCtrl', { 27 | $scope: $scope, 28 | userService: { 29 | currentUser: function() { 30 | return user; 31 | } 32 | }, 33 | loginService: {} 34 | }); 35 | 36 | // flush promises 37 | $rootScope.$apply(); 38 | }); 39 | 40 | it('should compile', function() { 41 | expect(elem.hasClass('ml-user')).to.eq(true); 42 | }); 43 | 44 | }); 45 | })(); 46 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/ml-user.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 7 | 8 |
9 | 15 |
16 |
17 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/profile.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.user') 5 | .config(["MLRestProvider", function (MLRestProvider) { 6 | // Make MLRest target url start with the page's base href (proxy) 7 | MLRestProvider.setPrefix(angular.element(document.querySelector('base')).attr('href')+'v1'); 8 | }]) 9 | .controller('ProfileCtrl', ProfileCtrl); 10 | 11 | ProfileCtrl.$inject = ['$scope', '$state', 'MLRest', 'userService', 'ngToast', '$rootScope']; 12 | 13 | function ProfileCtrl($scope, $state, mlRest, userService, toast, $rootScope) { 14 | var ctrl = this; 15 | angular.extend(ctrl, { 16 | user: checkUser(userService.currentUser()), 17 | newEmail: '', 18 | addEmail: addEmail, 19 | removeEmail: removeEmail, 20 | submit: submit 21 | }); 22 | 23 | function addEmail() { 24 | if (ctrl.user) { 25 | if (!ctrl.newEmail || hasEmailInputError()) { 26 | return; 27 | } 28 | ctrl.user.profile = ctrl.user.profile || {}; 29 | if (!ctrl.user.profile.emails) { 30 | ctrl.user.profile.emails = []; 31 | } 32 | ctrl.user.profile.emails.push(ctrl.newEmail.trim()); 33 | ctrl.newEmail = ''; 34 | } 35 | } 36 | 37 | function removeEmail(index) { 38 | if (ctrl.user.profile && ctrl.user.profile.emails) { 39 | ctrl.user.profile.emails.splice(index, 1); 40 | } 41 | } 42 | 43 | function hasEmailInputError() { 44 | try { 45 | return $scope.profileForm.newEmail.$error.email === true; 46 | } 47 | catch (e) { 48 | return false; 49 | } 50 | } 51 | 52 | function submit(form) { 53 | if (form.$valid && ctrl.user.profile) { 54 | addEmail(); 55 | 56 | if (ctrl.user.profile.emails) { 57 | _.pull(ctrl.user.profile.emails, ''); 58 | } 59 | 60 | mlRest.updateDocument({ 61 | user: ctrl.user.profile 62 | }, { 63 | format: 'json', 64 | uri: '/api/users/' + ctrl.user.username + '.json' 65 | }).then(function(data) { 66 | $rootScope.$broadcast('loginService:profile-changed'); 67 | toast.success('Submitted'); 68 | $state.go('root.landing'); 69 | }, function(response) { 70 | toast.danger(response.data); 71 | }); 72 | } 73 | } 74 | 75 | $scope.$watch(userService.currentUser, function(newValue) { 76 | ctrl.user = checkUser(newValue); 77 | }); 78 | 79 | function checkUser(newValue) { 80 | var user = angular.copy(newValue); 81 | if (user && user.profile && user.profile.emails) { 82 | if (!angular.isArray(user.profile.emails)) { 83 | user.profile.emails = [user.profile.emails]; 84 | } 85 | } 86 | return user; 87 | } 88 | } 89 | }()); 90 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/profile.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: ProfileCtrl', function () { 6 | 7 | var controller; 8 | 9 | var currentUser = { 10 | profile: { 11 | emails: [] 12 | } 13 | }; 14 | 15 | var currentState; 16 | 17 | beforeEach(function() { 18 | bard.appModule('app.user'); 19 | bard.inject('$controller', '$q', '$rootScope', 'MLRest', '$state', 'userService', 20 | 'loginService'); 21 | 22 | bard.mockService(MLRest, { 23 | _default: $q.when([]), 24 | updateDocument: $q.when() 25 | }); 26 | 27 | bard.mockService(userService, { 28 | currentUser: function() { 29 | return currentUser; 30 | } 31 | }); 32 | 33 | bard.mockService($state, { 34 | go: function(s) { 35 | currentState = s; 36 | } 37 | }); 38 | 39 | bard.mockService(loginService, { 40 | getAuthenticatedStatus: $q.when() 41 | }); 42 | 43 | }); 44 | 45 | beforeEach(function () { 46 | controller = $controller('ProfileCtrl', { 47 | $scope: $rootScope.$new() 48 | }); 49 | $rootScope.$apply(); 50 | }); 51 | 52 | it('should be created successfully', function () { 53 | expect(controller).to.be.defined; 54 | }); 55 | 56 | it('should not add a blank email', function() { 57 | var newEmail = ''; 58 | controller.newEmail = newEmail; 59 | expect(controller.user.profile.emails.length).to.eq(0); 60 | controller.addEmail(); 61 | $rootScope.$apply(); 62 | expect(controller.user.profile.emails.length).to.eq(0); 63 | }); 64 | 65 | it('should add a nonblank email', function() { 66 | var newEmail = 'test@test.com'; 67 | controller.newEmail = newEmail; 68 | expect(controller.user.profile.emails.length).to.eq(0); 69 | controller.addEmail(); 70 | $rootScope.$apply(); 71 | expect(controller.user.profile.emails.length).to.eq(1); 72 | expect(controller.user.profile.emails[0]).to.eq(newEmail); 73 | }); 74 | 75 | it('should remove an email', function() { 76 | controller.user.profile.emails = [ 77 | 'abc@def.com', 78 | 'def@ghi.com' 79 | ]; 80 | 81 | expect(controller.user.profile.emails.length).to.eq(2); 82 | controller.removeEmail(1); 83 | $rootScope.$apply(); 84 | expect(controller.user.profile.emails.length).to.eq(1); 85 | expect(controller.user.profile.emails[0]).to.eq('abc@def.com'); 86 | }); 87 | 88 | it('should not update the profile if form errors', function() { 89 | var form = {$valid:false}; 90 | controller.submit(form); 91 | $rootScope.$apply(); 92 | expect(currentState).to.not.be.defined; 93 | }); 94 | 95 | it('should update the profile', function() { 96 | var form = {$valid:true}; 97 | controller.submit(form); 98 | $rootScope.$apply(); 99 | expect(currentState).to.eq('root.landing'); 100 | }); 101 | 102 | }); 103 | }()); 104 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/profile.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Edit profile of {{$ctrl.user.username}}

5 |
6 |
7 |
8 |
9 |
10 | 11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 | 30 |
31 |
32 |
33 |
Not valid email!
34 |
35 | 36 | 37 |
38 |
39 | 45 |
46 |
47 |
48 |
49 |
50 | Cancel 51 | 52 |
53 |
54 |
55 |
56 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/user.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.user', [ 5 | // profile inject dependencies 6 | 'ui.router', 7 | 'ml.common', 8 | 'ngToast', 9 | 10 | // (ml-)user inject dependencies 11 | 'app.login', 12 | 13 | // profile/ml-user html dependencies 14 | 'ui.bootstrap' // for glyphicons 15 | ]); 16 | }()); 17 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/user.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.user') 5 | .factory('userService', UserService); 6 | 7 | UserService.$inject = ['$rootScope', '$q', 'loginService']; 8 | function UserService($rootScope, $q, loginService) { 9 | var _currentUser = null; 10 | 11 | function currentUser() { 12 | return _currentUser; 13 | } 14 | 15 | function getUser() { 16 | if (_currentUser) { 17 | return $q.resolve(_currentUser); 18 | } 19 | 20 | return loginService.getAuthenticatedStatus().then(currentUser); 21 | } 22 | 23 | function updateUser(response) { 24 | var data = response.data; 25 | 26 | if (data.authenticated === false) { 27 | return null; 28 | } 29 | 30 | // Copy all initially 31 | _currentUser = angular.copy(data); 32 | 33 | // Password property should not exist, delete anyhow just to be sure 34 | delete _currentUser.password; 35 | 36 | return _currentUser; 37 | } 38 | 39 | $rootScope.$on('loginService:login-success', function(e, user) { 40 | updateUser({ data: user }); 41 | }); 42 | 43 | $rootScope.$on('loginService:logout-success', function() { 44 | _currentUser = null; 45 | }); 46 | 47 | return { 48 | currentUser: currentUser, 49 | getUser: getUser 50 | }; 51 | } 52 | }()); 53 | -------------------------------------------------------------------------------- /templates/src/main/webapp/app/user/user.service.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function() { 3 | 'use strict'; 4 | 5 | describe('Service: userService', function() { 6 | 7 | var service; 8 | var _user = { 9 | data: { 10 | username: 'bob', 11 | authenticated: true 12 | } 13 | }; 14 | 15 | beforeEach(function() { 16 | bard.appModule('app.user'); 17 | bard.inject('$q', '$http', '$rootScope', '$state', 'loginService'); 18 | 19 | bard.mockService($http, { 20 | _default: $q.when([]), 21 | get: $q.when(_user), 22 | post: $q.when(_user) 23 | }); 24 | 25 | bard.mockService($state, { 26 | current: { name: 'root.search', params: {} }, 27 | go: function(stateName, stateParams) { 28 | this.current = { name: stateName, params: stateParams }; 29 | return $q.when(); 30 | }, 31 | reload: function() { 32 | return $q.when(); 33 | } 34 | }); 35 | 36 | bard.mockService(loginService, { 37 | getAuthenticatedStatus: $q.when() 38 | }); 39 | 40 | }); 41 | 42 | beforeEach(inject(function(_userService_) { 43 | service = _userService_; 44 | })); 45 | 46 | it('should be defined', function() { 47 | expect(service).to.be.defined; 48 | }); 49 | 50 | it('currentUser should not be defined', function() { 51 | expect(service.currentUser()).to.not.be.defined; 52 | }); 53 | 54 | it('should get the current logged in user - if loginService not init', function() { 55 | service.getUser().then(function(user) { 56 | expect(user).to.deep.eq(null); 57 | }); 58 | 59 | expect(loginService.getAuthenticatedStatus).to.have.been.calledOnce; 60 | 61 | $rootScope.$apply(); 62 | }); 63 | 64 | it('should update the current user when logged in using loginService', function(done) { 65 | $rootScope.$broadcast('loginService:login-success', {data:_user}); 66 | $rootScope.$apply(service); 67 | 68 | done(); 69 | expect(service.currentUser().name).to.eq('bob'); 70 | }); 71 | 72 | it('should not set user with invalid credentials', function () { 73 | _user.data.authenticated = false; 74 | $rootScope.$broadcast('loginService:login-success', {data:_user}); 75 | $rootScope.$apply(service); 76 | 77 | expect(service.currentUser().name).to.eq(undefined); 78 | }); 79 | 80 | it('should clear user after logout', function () { 81 | $rootScope.$broadcast('loginService:logout-success'); 82 | $rootScope.$apply(service); 83 | 84 | expect(service.currentUser()).to.not.be.defined; 85 | }); 86 | }); 87 | }()); 88 | -------------------------------------------------------------------------------- /templates/src/main/webapp/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= appName %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |

Oops! An error has occurred

20 |
    21 |
  • Timestamp:
  • 22 |
  • Path:
  • 23 |
  • HTTP Status:
  • 24 |
  • Error:
  • 25 |
  • Message:
  • 26 |
27 |

28 | Return to the home page 29 |

30 |
31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /templates/src/main/webapp/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /templates/src/main/webapp/images/MarkLogic-Powered-By.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/MarkLogic-Powered-By.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/MarkLogic-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/MarkLogic-white.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/favicon.ico -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/favicons/android-chrome-192x192.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/favicons/android-chrome-512x512.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #ffffff 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/favicons/favicon.ico -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "icons": [ 4 | { 5 | "src": "/images/favicons/android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "/images/favicons/android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#ffffff", 16 | "background_color": "#ffffff", 17 | "display": "standalone" 18 | } -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/favicons/mstile-150x150.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/favicons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 23 | 26 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /templates/src/main/webapp/images/flickr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/src/main/webapp/images/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /templates/src/main/webapp/images/linkedin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/src/main/webapp/images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/logo.jpg -------------------------------------------------------------------------------- /templates/src/main/webapp/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/logo.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/marklogic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/templates/src/main/webapp/images/marklogic.png -------------------------------------------------------------------------------- /templates/src/main/webapp/images/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/src/main/webapp/images/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /templates/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= appName %> 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /templates/src/main/webapp/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= appName %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |
24 |

<%= appName %>

25 |
Invalid username and password.
26 |
You have been logged out.
27 |
28 | 29 | 31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /templates/src/main/webapp/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | 4 | -------------------------------------------------------------------------------- /templates/src/main/webapp/styles/admin.less: -------------------------------------------------------------------------------- 1 | //== Colors 2 | // 3 | //## Gray and brand colors for use across Bootstrap. 4 | 5 | @gray-base: #000; 6 | @gray-darker: lighten(@gray-base, 13.5%); // #222 7 | @gray-dark: lighten(@gray-base, 20%); // #333 8 | @gray: lighten(@gray-base, 33.5%); // #555 9 | @gray-light: #4E5D6C; // #999 10 | @gray-lighter: #EBEBEB; // #eee 11 | 12 | @brand-primary: #2980b9; 13 | @brand-success: #1f8a70; 14 | @brand-info: #3498db; 15 | @brand-warning: #fd7400; 16 | @brand-danger: #e74c3c; 17 | 18 | //** Global textual link color. 19 | @link-color: @brand-primary; 20 | 21 | body.ng-scope { 22 | background-color: #fff; 23 | } 24 | 25 | div.content { 26 | background-color: #f8fcfc; 27 | } 28 | 29 | nav a, 30 | .navbar-default .navbar-nav > li > a, 31 | .navbar-default .navbar-nav > li > a:visited { 32 | color: #ecf0f1; 33 | } 34 | 35 | .navbar-default .navbar-collapse { 36 | color: #ecf0f1; 37 | } 38 | 39 | nav a:hover, 40 | nav a:active, 41 | .navbar-default .navbar-nav > li > a:hover, 42 | .navbar-default .navbar-nav > li > a:active { 43 | color: @brand-info; 44 | } 45 | 46 | a:hover, a:active, a:focus { 47 | outline: none; 48 | text-decoration: none; 49 | color: #72afd2; 50 | } 51 | 52 | nav.nav { 53 | background-color: #2c3e50; 54 | border-color: transparent; 55 | border: 0; 56 | height: 50px; 57 | } 58 | 59 | h1#logo { 60 | text-indent: 100%; 61 | white-space: nowrap; 62 | overflow: hidden; 63 | background: url('../images/MarkLogic-white.png') no-repeat; 64 | background-size: contain; 65 | min-height: 30px; 66 | height: 30px; 67 | margin: 0.1em 0; 68 | width: 160px; 69 | } 70 | 71 | /* header buttons */ 72 | 73 | .nav.navbar-nav a:before { 74 | font-family: FontAwesome; 75 | font-size: 24px; 76 | display: inline-block; 77 | } 78 | 79 | .nav.navbar-nav .home a:before { 80 | content: '\f015'; 81 | } 82 | 83 | .nav.navbar-nav .search a:before { 84 | content: '\f002'; 85 | } 86 | 87 | .nav.navbar-nav .create a:before { 88 | content: '\f044'; 89 | } 90 | 91 | .nav.navbar-nav a { 92 | font-size: 0; 93 | } 94 | 95 | /* facets */ 96 | ml-facets.col-md-3 { 97 | float: right; /* remove this selector if you want to be on left side */ 98 | } 99 | 100 | .facet { 101 | background: #fff; 102 | padding-bottom: 10px; 103 | border: 1px solid #ccc; 104 | border-top: 5px solid @brand-primary; 105 | margin: 5px 0; 106 | } 107 | 108 | .right-col h3 { 109 | border-bottom: 0 !important; 110 | color: #ecf0f1; 111 | margin-top: 0px; 112 | padding-top: 10px; 113 | } 114 | 115 | .right-col h3 { 116 | padding-left: 5px; 117 | } 118 | 119 | /* search */ 120 | .search .results .match .highlight { 121 | background-color: white; 122 | border: 1px dashed #5bc0de; 123 | border-radius: 0; 124 | color: @link-color;; 125 | padding: 0 2px 2px 2px; 126 | } 127 | 128 | /* footer edits */ 129 | footer { 130 | border-top: 1px solid @brand-primary; 131 | } 132 | 133 | .social-links, 134 | ul#menu-footer-navigation-logged-in { 135 | display: none; 136 | } 137 | 138 | /* border-radius */ 139 | @border-radius-base: 0; 140 | @border-radius-large: 0; 141 | @border-radius-small: 0; 142 | .results .pagination-ctrls .btn-group button#single-button { 143 | border-top-right-radius: 0; 144 | border-bottom-right-radius: 0; 145 | } -------------------------------------------------------------------------------- /templates/src/main/webapp/styles/main.less: -------------------------------------------------------------------------------- 1 | @import "../../resources/static/bower_components/bootstrap/less/bootstrap.less"; 2 | @import "../../resources/static/bower_components/font-awesome/less/font-awesome.less"; 3 | @import "../../resources/static/bower_components/ml-search-ng/dist/ml-search-ng-tpls.less"; 4 | @icon-font-path: "/fonts/"; 5 | @FontAwesomePath: "/fonts/"; 6 | 7 | /***************************** 8 | * UI Themes 9 | */ 10 | 11 | /* The default design, don't comment out */ 12 | @import "default.less"; 13 | 14 | /* 15 | * Uncomment one of the following designs for a different look.. 16 | */ 17 | 18 | /* Admin design - icons for top nativation 19 | @import "admin.less"; 20 | */ 21 | 22 | /* MLU design 23 | @import "mlu.less"; 24 | */ 25 | 26 | /* Theme-specific overrides, don't comment out */ 27 | @import "theme.less"; 28 | 29 | /***************************** 30 | * Project specific overrides 31 | */ 32 | 33 | // TODO: put your project specific overrides here! 34 | -------------------------------------------------------------------------------- /templates/src/main/webapp/styles/mlu.less: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #eee; 3 | } 4 | 5 | /*header*/ 6 | .navbar-brand { 7 | padding: 5px 10px 0 25px; 8 | } 9 | 10 | nav.nav { 11 | background-color: #222222; 12 | border-color: #080808; 13 | } 14 | 15 | h1#logo { 16 | text-indent: 100%; 17 | white-space: nowrap; 18 | overflow: hidden; 19 | background: url('/images/MarkLogic-white.png') no-repeat; 20 | background-size: contain; 21 | min-height: 30px; 22 | height: 30px; 23 | margin: 0.3em 0; 24 | width: 160px; 25 | } 26 | 27 | nav { 28 | background-color: #222; 29 | border-bottom: solid 1px #020202; 30 | 31 | &:after { 32 | display: block; 33 | content: ''; 34 | background: -webkit-linear-gradient(90deg, #222150 0%, #4466ab 45%, #4466ab 55%, #222150 100%); 35 | background: -moz-linear-gradient(90deg, #222150 0%, #4466ab 45%, #4466ab 55%, #222150 100%); 36 | background: -o-linear-gradient(90deg, #222150 0%, #4466ab 45%, #4466ab 55%, #222150 100%); 37 | background: linear-gradient(90deg, #222150 0%, #4466ab 45%, #4466ab 55%, #222150 100%); 38 | height: 0.94em; 39 | width: 100%; 40 | } 41 | .navbar-header { max-width: 50% } 42 | } 43 | 44 | .navbar-default .navbar-nav > li > a, 45 | .navbar-default .navbar-nav > li > a:hover, 46 | .navbar-default .navbar-nav > li > a:visited, 47 | .navbar-default .navbar-nav > li > a:active { 48 | color: #fff; 49 | } 50 | .navbar-default .navbar-nav > li > a:hover, 51 | .navbar-default .navbar-nav > li > a:active { 52 | color: #ccc; 53 | } 54 | 55 | .items { 56 | letter-spacing: 0.15em; 57 | } 58 | 59 | /* Logged in */ 60 | .welcome { 61 | color: #fff; 62 | } 63 | .welcome.authenticated { 64 | font-size: 80%; 65 | } 66 | 67 | /*style picker*/ 68 | .css-picker i { 69 | font-size: small; 70 | font-weight: normal; 71 | color: #fff; 72 | padding: 4px; 73 | } 74 | 75 | /*footer*/ 76 | footer { 77 | bottom: 0; 78 | width: 100%; 79 | padding: 1.3em 0; 80 | border-top: 1px solid #999999; 81 | color: #555555; 82 | text-align: center; 83 | background-color: #eee; 84 | 85 | .social-links { 86 | 87 | margin: auto; 88 | 89 | a { 90 | width: 25px; 91 | height: 25px; 92 | display: inline-block; 93 | margin-left: 1.5em; 94 | 95 | img { width: 100%; max-height: 100%; } 96 | &:first-child{ margin-left: 0 } 97 | } 98 | } 99 | 100 | .nav { 101 | 102 | padding: 1em 0; 103 | 104 | li { 105 | 106 | display: inline-block; 107 | 108 | a { 109 | color: #555; 110 | display: block; 111 | padding: 0 0 0 1em; 112 | 113 | &:after { 114 | content: '|'; 115 | display: inline-block; 116 | margin-left: 1em; 117 | } 118 | } 119 | 120 | &:last-child { 121 | a { 122 | padding-right: 1em; 123 | } 124 | a:after{ display: none; margin-left: 0; } 125 | 126 | } 127 | } 128 | } 129 | 130 | .copyright { font-size: 0.9em } 131 | } 132 | 133 | #footer { 134 | border-top: 1px solid; 135 | height: 51px; 136 | } 137 | 138 | footer#footer.powered { 139 | height: 60px; 140 | } 141 | -------------------------------------------------------------------------------- /templates/src/test/java/org/example/AbstractAppTest.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.test.context.ContextConfiguration; 4 | 5 | import com.marklogic.junit.NamespaceProvider; 6 | import com.marklogic.junit.spring.AbstractSpringTest; 7 | 8 | /** 9 | * This is intended to be the base class for all of your tests that hit MarkLogic. It initializes a Spring container via 10 | * the TestConfig class, which extends ml-junit's BasicTestConfig class. 11 | */ 12 | @ContextConfiguration(classes = { TestConfig.class }) 13 | public abstract class AbstractAppTest extends AbstractSpringTest { 14 | 15 | /** 16 | * Returns an app-specific namespace provider so that app-specific namespace prefixes can be used in XPath 17 | * assertions. 18 | */ 19 | @Override 20 | protected NamespaceProvider getNamespaceProvider() { 21 | return new AppNamespaceProvider(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /templates/src/test/java/org/example/AppNamespaceProvider.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import java.util.List; 4 | 5 | import org.jdom2.Namespace; 6 | 7 | import com.marklogic.junit.MarkLogicNamespaceProvider; 8 | 9 | public class AppNamespaceProvider extends MarkLogicNamespaceProvider { 10 | 11 | /** 12 | * Add namespaces to this list so that you can use the prefixes in assertions in your tests. 13 | */ 14 | @Override 15 | protected List buildListOfNamespaces() { 16 | List list = super.buildListOfNamespaces(); 17 | list.add(Namespace.getNamespace("sample", "http://marklogic.com/sample")); 18 | return list; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /templates/src/test/java/org/example/TestConfig.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.PropertySource; 7 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 8 | 9 | import com.marklogic.client.helper.DatabaseClientConfig; 10 | import com.marklogic.client.helper.DatabaseClientProvider; 11 | import com.marklogic.client.spring.SimpleDatabaseClientProvider; 12 | 13 | /** 14 | * Extends BasicTestConfig from ml-junit, which reads in properties from gradle.properties, to also read from 15 | * application.properties. 16 | */ 17 | @Configuration 18 | @PropertySource({ "file:gradle.properties", "file:src/main/resources/application.properties" }) 19 | public class TestConfig extends DevConfig { 20 | 21 | @Value("${mlTestRestPort}") 22 | private Integer mlTestRestPort; 23 | 24 | /** 25 | * Must be static so that it's loaded first and then used to parse the value annotations above. 26 | */ 27 | @Bean 28 | public static PropertySourcesPlaceholderConfigurer propertyConfigurer() { 29 | return new PropertySourcesPlaceholderConfigurer(); 30 | } 31 | 32 | /** 33 | * ml-junit depends on an instance of DatabaseClientProvider in order to connect to MarkLogic. 34 | */ 35 | @Bean 36 | public DatabaseClientProvider databaseClientProvider() { 37 | DatabaseClientConfig config = new DatabaseClientConfig(mlHost, mlTestRestPort, mlRestAdminUsername, 38 | mlRestAdminPassword); 39 | config.setDatabase(mlAppName + "-test-content"); 40 | return new SimpleDatabaseClientProvider(config); 41 | } 42 | } -------------------------------------------------------------------------------- /templates/src/test/java/org/example/WriteAndReadDocumentTest.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.junit.Test; 4 | 5 | import com.marklogic.client.io.Format; 6 | import com.marklogic.client.io.StringHandle; 7 | import com.marklogic.junit.Fragment; 8 | 9 | /** 10 | * Sample ml-junit test. Note that the database is cleared at the start of the test, but not at the end, so that you can 11 | * manually inspect documents after the test has completed. 12 | */ 13 | public class WriteAndReadDocumentTest extends AbstractAppTest { 14 | 15 | /** 16 | * This is a basic test - it loads an XML document using the MarkLogic Java API, and then it reads that document 17 | * back as a String using the MarkLogic Java API. That String is then parsed as an XML fragment, and with the 18 | * Fragment object, we can easily make a number of assertions. 19 | */ 20 | @Test 21 | public void writeAndReadDocument() { 22 | String xml = "JaneThis is a test"; 23 | getClient().newXMLDocumentManager().write("/jane.xml", new StringHandle(xml).withFormat(Format.XML)); 24 | 25 | xml = getClient().newXMLDocumentManager().read("/jane.xml", new StringHandle()).get(); 26 | Fragment frag = parse(xml); 27 | frag.assertElementValue("This is a basic assertion on the text node of a particular element", 28 | "/sample:person/sample:name", "Jane"); 29 | frag.assertElementExists("Can also assert on the presence of an element as expressed via XPath", 30 | "//sample:description[. = 'This is a test']"); 31 | 32 | // Example of using prettyPrint for debugging 33 | frag.prettyPrint(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /themes/3column/src/main/webapp/images/filetype_sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/3column/src/main/webapp/images/filetype_sprite.png -------------------------------------------------------------------------------- /themes/cards/src/main/webapp/app/detail/detail.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 26 | 29 | 30 |
31 |
32 |
33 | 34 | 37 | 48 |
49 |
50 | 51 |
52 |
Similar
53 |
54 | 55 |
56 |
57 |
58 |
59 | -------------------------------------------------------------------------------- /themes/cards/src/main/webapp/app/search/search-results.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |

6 | {{ result.label || result.uri }} 7 |

8 |
9 |
10 | 11 | {{ text.highlight || text }} 12 | 13 |
14 |
15 |
16 | 20 |
21 |
-------------------------------------------------------------------------------- /themes/cards/src/main/webapp/app/search/search.controller.js: -------------------------------------------------------------------------------- 1 | /* global MLSearchController */ 2 | (function () { 3 | 'use strict'; 4 | 5 | angular.module('app.search') 6 | .controller('SearchCtrl', SearchCtrl); 7 | 8 | SearchCtrl.$inject = ['$scope', '$location', 'MLSearchFactory']; 9 | 10 | // inherit from MLSearchController 11 | var superCtrl = MLSearchController.prototype; 12 | SearchCtrl.prototype = Object.create(superCtrl); 13 | 14 | function SearchCtrl($scope, $location, searchFactory) { 15 | var ctrl = this; 16 | 17 | superCtrl.constructor.call(ctrl, $scope, $location, searchFactory.newContext({ 18 | pageLength: 12 19 | })); 20 | 21 | ctrl.init(); 22 | 23 | ctrl.setSnippet = function(type) { 24 | ctrl.mlSearch.setSnippet(type); 25 | ctrl.search(); 26 | }; 27 | } 28 | }()); 29 | -------------------------------------------------------------------------------- /themes/cards/src/main/webapp/app/search/search.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 |
7 | 8 | 26 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/dashboard/content-box.directive.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Directive: content-box', function () { 6 | 7 | var elem; 8 | 9 | beforeEach(function() { 10 | bard.appModule('app.dashboard'); 11 | bard.inject('$compile', '$rootScope'); 12 | }); 13 | 14 | beforeEach(function() { 15 | var $scope = $rootScope.$new(); 16 | elem = angular.element( 17 | '' 18 | ); 19 | $compile(elem)($scope); 20 | $scope.$digest(); 21 | 22 | // flush promises 23 | $rootScope.$apply(); 24 | }); 25 | 26 | it('should compile', function() { 27 | expect(elem.hasClass('box')).to.eq(true); 28 | }); 29 | 30 | }); 31 | })(); 32 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/dashboard/content-box.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.dashboard') 5 | .directive('contentBox', ContentBoxDirective); 6 | 7 | function ContentBoxDirective() { 8 | return { 9 | restrict: 'E', 10 | transclude: true, 11 | replace: true, 12 | scope: { 13 | boxTitle: '@', 14 | boxBordered: '@', 15 | collapsed: '@' 16 | }, 17 | link: function($scope, $elem, $attrs) { 18 | $scope.hasBorder = $scope.boxBordered !== 'false' && $scope.boxBordered !== 'no'; 19 | 20 | $scope.toggleCollapsed = function() { 21 | $scope.isCollapsed = !$scope.isCollapsed; 22 | }; 23 | 24 | $scope.$watch(function() { return $attrs.collapsed; }, function(newVal) { 25 | newVal = '' + newVal; 26 | $scope.isCollapsed = newVal === 'true' || newVal === 'yes'; 27 | }); 28 | }, 29 | /* jshint multistr: true */ 30 | /* jscs: disable */ 31 | template: '
\ 32 |
\ 33 |

{{ boxTitle }}

\ 34 |
\ 35 | \ 36 | \ 37 |
\ 38 |
\ 39 |
\ 40 |
' 41 | /* jscs: enable */ 42 | }; 43 | } 44 | 45 | })(); 46 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/dashboard/dashboard.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.dashboard', [ 5 | // html dependencies 6 | 'ui.bootstrap' 7 | ]); 8 | }()); 9 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/landing/landing.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: LandingCtrl', function () { 6 | 7 | var controller; 8 | 9 | var _user = { 10 | data: { 11 | username: 'bob', 12 | authenticated: true 13 | } 14 | }; 15 | var results = [{ 16 | uri: 'abc' 17 | }, { 18 | uri: 'def' 19 | }]; 20 | 21 | beforeEach(function() { 22 | bard.appModule('app.landing'); 23 | bard.inject('$controller', '$rootScope', 24 | 'MLSearchFactory', 'MLRest', '$q'); 25 | 26 | bard.mockService(MLRest, { 27 | search: $q.when({ 28 | data: { 29 | results: results 30 | } 31 | }) 32 | }); 33 | 34 | }); 35 | 36 | beforeEach(function () { 37 | controller = $controller('LandingCtrl', { 38 | $scope: $rootScope.$new(), 39 | $rootScope: $rootScope, 40 | MLSearchFactory: MLSearchFactory 41 | }); 42 | $rootScope.$apply(); 43 | }); 44 | 45 | it('should be created successfully', function () { 46 | expect(controller).to.be.defined; 47 | }); 48 | 49 | it('should run a search at login', function(done) { 50 | $rootScope.$broadcast('loginService:login-success', {data:_user}); 51 | $rootScope.$apply(controller); 52 | done(); 53 | expect(controller.mlSearch.results.results).to.eq(results); 54 | }); 55 | 56 | it('should succeed twice', function(done) { 57 | $rootScope.$broadcast('loginService:login-success', {data:_user}); 58 | $rootScope.$apply(controller); 59 | done(); 60 | $rootScope.$broadcast('loginService:login-success', {data:_user}); 61 | $rootScope.$apply(controller); 62 | done(); 63 | expect(controller.mlSearch.results.results).to.eq(results); 64 | // mostly for code coverage 65 | }); 66 | 67 | it('should flush results at logout', function(done) { 68 | $rootScope.$broadcast('loginService:logout-success'); 69 | $rootScope.$apply(controller); 70 | done(); 71 | expect(controller.mlSearch).to.eq(null); 72 | }); 73 | }); 74 | }()); 75 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/landing/landing.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.landing', [ 5 | // inject dependencies 6 | 'ml.search', 7 | 'app.user', 8 | 9 | // html dependencies 10 | 'app.dashboard', 11 | 'ml.highcharts', 12 | 'ui.bootstrap' 13 | ]); 14 | }()); 15 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/navigation/navigation.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.navigation', [ 5 | // inject dependencies 6 | 'ui.router' 7 | ]); 8 | }()); 9 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/navigation/navigation.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.navigation') 5 | .factory('navService', NavigationService); 6 | 7 | NavigationService.$injector = ['$rootScope', '$state']; 8 | 9 | function NavigationService($rootScope, $state) { 10 | var service = {}, links, active, activeLabel, linkAreas = {}; 11 | 12 | $rootScope.$on('$stateChangeSuccess', function(evt, target) { 13 | service.setCurrentState(target); 14 | }); 15 | 16 | service.registerStates = function(states) { 17 | var s, lbl; 18 | 19 | links = []; 20 | linkAreas = {}; 21 | for (var i = 0; i < states.length; i++) { 22 | s = states[i]; 23 | lbl = s.navLabel || { text: s.name }; 24 | 25 | var link = { state: s.name, label: lbl , url: s.url }; 26 | links.push(link); 27 | // group it if area is specified 28 | if (lbl.area) { 29 | if (!linkAreas[lbl.area]) { 30 | linkAreas[lbl.area] = []; 31 | } 32 | linkAreas[lbl.area].push(link); 33 | } 34 | } 35 | }; 36 | 37 | service.setCurrentState = function($state) { 38 | active = $state.name; 39 | activeLabel = $state.navLabel; 40 | }; 41 | 42 | service.isActive = function(stateName, areaName) { 43 | return areaName ? 44 | (activeLabel.area === areaName && active === stateName) : 45 | (active === stateName); 46 | }; 47 | 48 | service.getLinks = function(area) { 49 | return area ? linkAreas[area] : links; 50 | }; 51 | 52 | // TODO: redundant code? 53 | // service.toXSDateTime = function(jsDate) { 54 | // if (!jsDate) { 55 | // return; 56 | // } 57 | // var xsd = jsDate.toISOString(); 58 | // return xsd.slice(0, -1); 59 | // //xsd += '+' + (jsDate.getTimezoneOffset() / 60) + ':00'; 60 | // //return xsd; 61 | // }; 62 | 63 | // set up the current state in case we missed the event 64 | if ($state && $state.current) { 65 | service.setCurrentState($state.current); 66 | } 67 | 68 | service.gotoLink = function(link) { 69 | $state.go(link.state); 70 | }; 71 | 72 | service.showSidebar = true; 73 | 74 | service.toggleSidebar = function () { 75 | service.showSidebar = !service.showSidebar; 76 | }; 77 | 78 | return service; 79 | } 80 | 81 | })(); 82 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/navigation/navigation.service.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Service: navService', function () { 6 | 7 | var service; 8 | 9 | var testStates = [{ 10 | name: 'test', 11 | navLabel: { 12 | test: 'Test', 13 | area: 'testarea' 14 | }, 15 | url: '/test' 16 | },{ 17 | name: 'test2', 18 | navLabel: { 19 | test: 'Test 2', 20 | area: 'testarea' 21 | }, 22 | url: '/test2' 23 | },{ 24 | name: 'test3', 25 | navLabel: { 26 | test: 'Test 3' 27 | }, 28 | url: '/test3' 29 | },{ 30 | name: 'test4', 31 | url: '/test4' 32 | }]; 33 | 34 | beforeEach(function() { 35 | bard.appModule('app.navigation'); 36 | bard.inject('$state', '$q', '$rootScope'); 37 | bard.mockService($state, { 38 | current: { name: 'root.landing', params: {} }, 39 | go: function(stateName, stateParams) { 40 | this.current = { name: stateName, params: stateParams }; 41 | return $q.when(); 42 | } 43 | }); 44 | 45 | }); 46 | 47 | beforeEach(inject(function (_navService_) { 48 | service = _navService_; 49 | service.registerStates(testStates); 50 | })); 51 | 52 | it('should be defined', function () { 53 | expect(service).to.be.defined; 54 | }); 55 | 56 | it('should act on state change', function(done) { 57 | $rootScope.$broadcast('$stateChangeSuccess', testStates[1]); 58 | $rootScope.$apply(service); 59 | done(); 60 | expect(service.isActive(testStates[1].name, testStates[1].navLabel.area)).to.eq(true); 61 | 62 | $rootScope.$broadcast('$stateChangeSuccess', testStates[4]); 63 | $rootScope.$apply(service); 64 | done(); 65 | expect(service.isActive(testStates[4].name)).to.eq(true); 66 | }); 67 | 68 | it('should get links', function () { 69 | expect(service.getLinks().length).to.eq(4); 70 | }); 71 | 72 | it('should get links for an area', function () { 73 | expect(service.getLinks('testarea').length).to.eq(2); 74 | }); 75 | 76 | it('should toggle the sidebar', function () { 77 | service.showSidebar = false; 78 | service.toggleSidebar(); 79 | expect(service.showSidebar).to.eq(true); 80 | }); 81 | 82 | it('should goto link', function (done) { 83 | service.gotoLink(service.getLinks()[1]); 84 | $rootScope.$apply(service); 85 | done(); 86 | expect(service.isActive(testStates[1].name, testStates[1].navLabel.area)).to.eq(true); 87 | }); 88 | 89 | }); 90 | }()); 91 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/root/root.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.root') 5 | .controller('RootCtrl', RootCtrl); 6 | 7 | RootCtrl.$inject = ['messageBoardService', 'userService', '$scope', 8 | '$state', 'appConfig', 'loginService', 'navService']; 9 | 10 | function RootCtrl(messageBoardService, userService, $scope, 11 | $state, appConfig, loginService, navService) { 12 | 13 | var rootCtrl = this; 14 | rootCtrl.currentYear = new Date().getUTCFullYear(); 15 | rootCtrl.messageBoardService = messageBoardService; 16 | angular.extend(rootCtrl, appConfig); 17 | 18 | $scope.$watch(userService.currentUser, function(newValue) { 19 | rootCtrl.currentUser = newValue; 20 | }); 21 | 22 | $scope.$watch(function() { 23 | return $state.current.name; 24 | }, function(newValue) { 25 | rootCtrl.currentState = newValue; 26 | }); 27 | 28 | // allow logout from custom header user section 29 | rootCtrl.loginService = loginService; 30 | 31 | // call this to register the available states 32 | navService.registerStates($state.get()); 33 | 34 | rootCtrl.navService = navService; 35 | } 36 | }()); 37 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/root/root.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: RootCtrl', function () { 6 | 7 | var controller; 8 | 9 | beforeEach(function() { 10 | bard.appModule('app.root'); 11 | bard.inject('$controller', '$rootScope'); 12 | 13 | var msg = {}; 14 | var user = { name: 'Guest' }; 15 | 16 | controller = $controller('RootCtrl', { 17 | $scope: $rootScope.$new(), 18 | messageBoardService: { 19 | message: function() { 20 | return msg; 21 | } 22 | }, 23 | userService: { 24 | currentUser: function() { 25 | return user; 26 | } 27 | }, 28 | $state: { 29 | get: function() {}, 30 | current: { 31 | name: 'root' 32 | } 33 | }, 34 | loginService: {}, 35 | navService: { 36 | registerStates: function() {} 37 | } 38 | }); 39 | 40 | $rootScope.$apply(); 41 | }); 42 | 43 | it('should be created successfully', function () { 44 | expect(controller).to.be.defined; 45 | }); 46 | 47 | }); 48 | }()); 49 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/app/root/root.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.root', [ 5 | // inject dependencies 6 | 'app.login', 7 | 'app.messageBoard', 8 | 'app.navigation', 9 | 'app.route', 10 | 'app.user', 11 | 'ui.router', 12 | 13 | // child states 14 | 'app.create', 15 | 'app.detail', 16 | 'app.landing', 17 | 'app.search', 18 | 'app.user' 19 | ]); 20 | }()); 21 | -------------------------------------------------------------------------------- /themes/dashboard/src/main/webapp/styles/theme.less: -------------------------------------------------------------------------------- 1 | @import (inline) "./adminLTE.css"; 2 | 3 | @icon-font-path: "../fonts/"; 4 | 5 | /* If using a dark background, you need to use the white ML logo 6 | .logo-lg #logo { 7 | background: url('/images/MarkLogic-white.png') no-repeat; 8 | background-size: 95%; 9 | } 10 | */ 11 | 12 | body { 13 | background-color: #fff; 14 | } 15 | 16 | a { 17 | cursor: pointer; 18 | cursor: hand; 19 | } 20 | 21 | h1 { 22 | margin-top: 0px; 23 | padding: 30px 15px 15px 15px; 24 | font-size: 24px; 25 | } 26 | 27 | .search.row { 28 | background-color: #fff; 29 | } 30 | 31 | span.search-input-icon.search-input-clear.form-control-feedback { 32 | margin-right: 35px; 33 | color: #000; 34 | z-index: 10; 35 | } 36 | 37 | .row.detail { 38 | padding-top: 20px; 39 | } 40 | 41 | .main-header #logo { 42 | text-indent: 99999px; 43 | } 44 | 45 | .pre-xml { 46 | background-color: #ecf0f5; 47 | } 48 | 49 | footer { 50 | .social-links, #menu-footer-navigation-logged-in { 51 | display: none; 52 | } 53 | } 54 | 55 | form.ml-input.ml-search { 56 | padding-top: 15px; 57 | } 58 | 59 | .create form { 60 | padding-bottom: 50px; 61 | } 62 | 63 | .powered { 64 | background: transparent url(../images/MarkLogic-Powered-By.png) no-repeat scroll right; 65 | } 66 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/detail/detail.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: DetailCtrl', function () { 6 | 7 | var controller1, controller2, controller3, controller4, controller5; 8 | var xmldoc, jsondoc, txtdoc, bindoc, otherdoc; 9 | 10 | beforeEach(function() { 11 | bard.appModule('app.detail'); 12 | bard.inject('$controller', '$rootScope', 'MLRest', '$q'); 13 | 14 | bard.mockService(MLRest, { 15 | deleteDocument: function() { 16 | return $q.when(arguments[0].uri); 17 | } 18 | }); 19 | }); 20 | 21 | beforeEach(function () { 22 | // xml 23 | xmldoc = { 24 | headers: function() { return 'application/xml'; }, 25 | data: 'hi' 26 | }; 27 | controller1 = $controller('DetailCtrl', { 28 | doc: xmldoc, 29 | $scope: $rootScope.$new(), 30 | $stateParams: { uri: '/doc.xml' } 31 | }); 32 | 33 | // json 34 | jsondoc = { 35 | headers: function() { return 'application/json'; }, 36 | data: { 37 | name: 'hi', 38 | location: { 39 | latitude: 0, 40 | longitude: 0 41 | } 42 | } 43 | }; 44 | controller2 = $controller('DetailCtrl', { 45 | doc: jsondoc, 46 | $scope: $rootScope.$new(), 47 | $stateParams: { uri: '/doc.json' } 48 | }); 49 | 50 | // txt 51 | txtdoc = { 52 | headers: function() { return 'text/plain'; }, 53 | data: 'hi' 54 | }; 55 | controller3 = $controller('DetailCtrl', { 56 | doc: txtdoc, 57 | $scope: $rootScope.$new(), 58 | $stateParams: { uri: '/doc.txt' } 59 | }); 60 | 61 | // bin 62 | bindoc = { 63 | headers: function() { return 'application/pdf'; }, 64 | data: 'xxxx' 65 | }; 66 | controller4 = $controller('DetailCtrl', { 67 | doc: bindoc, 68 | $scope: $rootScope.$new(), 69 | $stateParams: { uri: '/doc.pdf' } 70 | }); 71 | 72 | // unknown 73 | otherdoc = { 74 | headers: function() { return 'unknown'; }, 75 | data: 'xxxx' 76 | }; 77 | controller5 = $controller('DetailCtrl', { 78 | doc: otherdoc, 79 | $scope: $rootScope.$new(), 80 | $stateParams: { uri: '/doc.other' } 81 | }); 82 | 83 | $rootScope.$apply(); 84 | }); 85 | 86 | it('should be created successfully', function () { 87 | expect(controller1).to.be.defined; 88 | expect(controller2).to.be.defined; 89 | expect(controller3).to.be.defined; 90 | expect(controller4).to.be.defined; 91 | expect(controller5).to.be.defined; 92 | }); 93 | 94 | it('should have the doc data we gave it', function() { 95 | expect(controller1.doc).to.eq(xmldoc.data); 96 | expect(controller2.doc).to.eq(jsondoc.data); 97 | expect(controller3.doc).to.eq(txtdoc.data); 98 | expect(controller4.doc).to.eq(bindoc.data); 99 | expect(controller5.doc).to.eq(otherdoc.data); 100 | }); 101 | 102 | it('should delete docs', function() { 103 | controller1.delete(); 104 | controller2.delete(); 105 | controller3.delete(); 106 | controller4.delete(); 107 | controller5.delete(); 108 | // no expects, just for code coverage 109 | // TODO: then branch of deleteDocument not reached according code coverage?? 110 | }); 111 | 112 | }); 113 | }()); 114 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/detail/detail.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 | 8 | 19 |
20 |
21 | 43 | 46 | 47 |
48 |
49 | 50 |
51 |
Similar
52 |
53 | 54 |
55 |
56 |
57 |
58 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/detail/detail.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.detail', [ 5 | // inject dependencies 6 | 'cb.x2js', 7 | 'ml.common', // Using MLRest for delete 8 | 'ngToast', // Showing toast on delete 9 | 'ui.router', 10 | 'app.map', 11 | 12 | // html dependencies 13 | 'app.similar', 14 | 'ngJsonExplorer', 15 | 'mwl.confirm', // for delete confirmation popups 16 | 'ui.bootstrap' 17 | ]); 18 | }()); 19 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/login/login-full.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/map/map-manager.service.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Service: MLUiGmapManager', function () { 6 | 7 | var service; 8 | 9 | var results = [{ 10 | uri: '/test1.json', 11 | extracted: { 12 | content: [{ 13 | location: { 14 | latitude: 1, 15 | longitude: 1 16 | } 17 | }] 18 | } 19 | },{ 20 | uri: '/test2.json', 21 | extracted: { 22 | content: [{ 23 | location: { 24 | latitude: 2, 25 | longitude: 2 26 | } 27 | }] 28 | } 29 | },{ 30 | uri: '/test3.json', 31 | extracted: { 32 | content: [{ 33 | location: { 34 | latitude: 3, 35 | longitude: 3 36 | } 37 | }] 38 | } 39 | }]; 40 | var facets = { 41 | geo1: { 42 | boxes: [{ 43 | s: -1, 44 | w: -1, 45 | n: 1, 46 | e: 1, 47 | count: 10 48 | }] 49 | }, 50 | geo2: { 51 | boxes: [{ 52 | s: -2, 53 | w: -2, 54 | n: 2, 55 | e: 2, 56 | count: 20 57 | }] 58 | } 59 | }; 60 | 61 | beforeEach(function() { 62 | bard.appModule('app.map'); 63 | }); 64 | 65 | beforeEach(inject(function (_MLUiGmapManager_) { 66 | service = _MLUiGmapManager_; 67 | })); 68 | 69 | it('should be defined', function () { 70 | expect(service).to.be.defined; 71 | }); 72 | 73 | it('should init', function () { 74 | service.init(0, 0); 75 | }); 76 | 77 | it('should set result markers', function () { 78 | service.markerMode = 'results'; 79 | service.setResultMarkers(results); 80 | expect(service.getMarkers().length).to.eq(3); 81 | }); 82 | 83 | it('should set facet markers', function () { 84 | service.markerMode = 'facets'; 85 | service.setFacetMarkers(facets); 86 | expect(service.getMarkers().length).to.eq(2); 87 | }); 88 | 89 | it('should watchBounds', function () { 90 | service.watchBounds(); 91 | }); 92 | 93 | it('should watchDrawings', function () { 94 | service.watchDrawings(); 95 | }); 96 | 97 | it('should resetMap', function () { 98 | service.resetMap(); 99 | }); 100 | 101 | }); 102 | }()); 103 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/map/map-utils.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.map') 5 | .config(["MLRestProvider", function (MLRestProvider) { 6 | // Make MLRestProvider target url start with the page's base href (proxy) 7 | MLRestProvider.setPrefix(angular.element(document.querySelector('base')).attr('href')+'v1'); 8 | }]) 9 | .service('mapUtils', MapUtilsFactory); 10 | 11 | function MapUtilsFactory() { 12 | var service = {}, width = window.innerWidth; 13 | 14 | service.isMobile = function() { 15 | return service.isXS() || service.isSM(); 16 | }; 17 | 18 | service.isXS = function() { 19 | return width < 768; // match boostrap xs 20 | }; 21 | 22 | service.isSM = function() { 23 | return width < 992; // match bootrap sm 24 | }; 25 | 26 | return service; 27 | } 28 | 29 | })(); 30 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/map/map-utils.service.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Service: mapUtils', function () { 6 | 7 | var service; 8 | 9 | beforeEach(function() { 10 | bard.appModule('app.map'); 11 | }); 12 | 13 | beforeEach(inject(function (_mapUtils_) { 14 | service = _mapUtils_; 15 | })); 16 | 17 | it('should be defined', function () { 18 | expect(service).to.be.defined; 19 | }); 20 | 21 | it('should check isMobile', function () { 22 | expect(service.isMobile()).to.eq(true); 23 | }); 24 | 25 | }); 26 | }()); 27 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/map/map.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.map', [ 5 | // inject dependencies 6 | 'uiGmapgoogle-maps' 7 | ]) 8 | .config(["MLRestProvider", function (MLRestProvider) { 9 | // Make MLRestProvider target url start with the page's base href (proxy) 10 | MLRestProvider.setPrefix(angular.element(document.querySelector('base')).attr('href')+'v1'); 11 | }]) 12 | .config(['uiGmapGoogleMapApiProvider', function(uiGmapGoogleMapApiProvider) { 13 | var libs = uiGmapGoogleMapApiProvider.options.libraries; 14 | if (libs === '') { 15 | libs = 'drawing'; 16 | } else { 17 | libs = libs.split(',').push('drawing').join(','); 18 | } 19 | uiGmapGoogleMapApiProvider.configure({ 20 | libraries: libs 21 | }); 22 | }]); 23 | }()); 24 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/root/infoWindow-details.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
{{key}}
5 | 6 |
{{ val !== '' ? val : ' ' }}
7 | 8 | 9 | 10 |
{{ val.join(', ') }}
11 | 12 |
 
13 | 14 |
15 | 16 |
17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/root/infoWindow.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |

{{ parameter.name || parameter.label }}

6 |
7 |
8 |
9 |
10 |
11 |
-------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/root/root.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: RootCtrl', function () { 6 | 7 | var controller; 8 | 9 | beforeEach(function() { 10 | bard.appModule('app.root'); 11 | bard.inject('$controller', '$rootScope', '$templateCache'); 12 | 13 | $templateCache.put( 'app/root/infoWindow.html', 14 | '
' 15 | ); 16 | 17 | var msg = {}; 18 | var user = { name: 'Guest' }; 19 | 20 | controller = $controller('RootCtrl', { 21 | $scope: $rootScope.$new(), 22 | messageBoardService: { 23 | message: function() { 24 | return msg; 25 | } 26 | }, 27 | userService: { 28 | currentUser: function() { 29 | return user; 30 | } 31 | }, 32 | $state: { 33 | get: function() {}, 34 | current: { 35 | name: 'root' 36 | } 37 | } 38 | }); 39 | 40 | $rootScope.$apply(); 41 | }); 42 | 43 | it('should be created successfully', function () { 44 | expect(controller).to.be.defined; 45 | }); 46 | 47 | }); 48 | }()); 49 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/root/root.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 28 | 29 |
30 | 37 | 44 | 45 | 52 | 53 | 57 | 58 | 59 | 60 |
61 | 62 | 67 |
68 | 69 |
70 | 73 |
74 |
75 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/root/root.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.root', [ 5 | // inject dependencies 6 | 'app.login', 7 | 'app.map', 8 | 'app.messageBoard', 9 | 'app.route', 10 | 'app.user', 11 | 'ui.router', 12 | 13 | // child states 14 | 'app.create', 15 | 'app.detail', 16 | 'app.landing', 17 | 'app.search', 18 | 'app.user' 19 | ]); 20 | }()); 21 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/search/search.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | describe('Controller: SearchCtrl', function () { 6 | 7 | var controller; 8 | 9 | var results = [ 10 | { 11 | uri: 'abc' 12 | }, 13 | { 14 | uri: 'def' 15 | } 16 | ]; 17 | 18 | beforeEach(function() { 19 | bard.appModule('app.search'); 20 | bard.inject('$controller', '$q', '$rootScope', '$location', 21 | 'MLSearchFactory', 'MLRest'); 22 | 23 | bard.mockService(MLRest, { 24 | search: $q.when({ 25 | data: { 26 | results: results 27 | } 28 | }) 29 | }); 30 | 31 | }); 32 | 33 | beforeEach(function () { 34 | controller = $controller('SearchCtrl', { $scope: $rootScope.$new() }); 35 | $rootScope.$apply(); 36 | }); 37 | 38 | it('should be created successfully', function () { 39 | expect(controller).to.be.defined; 40 | }); 41 | 42 | // TODO: needs to run on login-success event.. 43 | // it('should update the current user if it changes', function() { 44 | // expect(controller.currentUser).to.not.be.defined; 45 | // }); 46 | 47 | it('should run an initial search', function() { 48 | expect(controller.response.results).to.eq(results); 49 | }); 50 | 51 | it('should run a search', function() { 52 | controller.response = null; 53 | controller.search('stuff'); 54 | $rootScope.$apply(); 55 | expect(controller.response.results).to.eq(results); 56 | }); 57 | 58 | it('should search on snippet change', function() { 59 | controller.response = null; 60 | controller.setSnippet('my-snippet'); 61 | $rootScope.$apply(); 62 | expect(controller.response.results).to.eq(results); 63 | }); 64 | }); 65 | }()); 66 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/search/search.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 | 9 | 12 |
13 |
14 | 17 |
18 |
19 | 22 |
23 |
24 |
25 | 26 | 44 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/search/search.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.search', [ 5 | // inject dependencies 6 | 'app.map', 7 | 'ml.search', 8 | 9 | // html dependencies 10 | 'app.snippet', 11 | 'ml.search.tpls', 12 | 'ui.bootstrap', 13 | 'ui.router' 14 | ]); 15 | }()); 16 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/app/user/profile.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

Edit profile of {{$ctrl.user.username}}

5 |
6 |
7 |
8 |
9 |
10 | 11 | 12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 | 23 | 26 |
27 |
28 |
29 |
Not valid email!
30 |
31 | 32 | 33 |
34 |
35 | 41 |
42 |
43 |
44 |
45 |
46 | Cancel 47 | 48 |
49 |
50 |
51 |
52 | -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/blue-cluster-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/blue-cluster-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/blue-dot-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/blue-dot-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/green-cluster-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/green-cluster-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/green-dot-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/green-dot-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/grey-cluster-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/grey-cluster-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/grey-dot-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/grey-dot-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/purple-cluster-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/purple-cluster-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/purple-dot-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/purple-dot-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/red-cluster-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/red-cluster-marker.png -------------------------------------------------------------------------------- /themes/map/src/main/webapp/images/red-dot-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjrudin/slush-marklogic-spring-boot/34827cb4255b9d09e77292f809a55fef92e001b8/themes/map/src/main/webapp/images/red-dot-marker.png --------------------------------------------------------------------------------