├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── example │ │ └── demo │ │ ├── DemoApplication.java │ │ ├── TestController.java │ │ ├── config │ │ └── FlowableConfig.java │ │ ├── controller │ │ └── IdmController.java │ │ └── delegate │ │ ├── CallExternalSystemDelegate.java │ │ └── SendRejectionMail.java └── resources │ ├── application.yml │ ├── log4j.properties │ ├── processes │ └── holiday-request.bpmn20.xml │ └── static │ └── flowable-modeler │ ├── editor-app │ ├── css │ │ └── style.css │ ├── define-data-controller.js │ ├── editor-config.js │ ├── editor-controller.js │ ├── editor-utils.js │ ├── editor.html │ ├── editormanager.js │ ├── eventbus.js │ ├── header-controller.js │ ├── plugins.xml │ ├── process-navigator-controller.js │ ├── select-shape-controller.js │ ├── stencil-controller.js │ ├── toolbar-controller.js │ └── tour.js │ ├── i18n │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── pt-BR.json │ └── zh-CN.json │ ├── scripts │ ├── app-cfg.js │ ├── app.js │ ├── common │ │ ├── controllers │ │ │ └── about.js │ │ ├── directives.js │ │ ├── providers-config.js │ │ └── services │ │ │ ├── recursion-helper.js │ │ │ └── resource-service.js │ ├── configuration │ │ ├── app-definition-toolbar-default-actions.js │ │ ├── app-definition-toolbar.js │ │ ├── decision-table-toolbar-default-actions.js │ │ ├── decision-table-toolbar.js │ │ ├── form-builder-toolbar-default-actions.js │ │ ├── form-builder-toolbar.js │ │ └── url-config.js │ ├── controllers │ │ ├── app-definition-builder.js │ │ ├── app-definition-toolbar-controller.js │ │ ├── app-definition.js │ │ ├── app-definitions.js │ │ ├── casemodel.js │ │ ├── casemodels.js │ │ ├── decision-table-editor.js │ │ ├── decision-table-toolbar-controller.js │ │ ├── decision-table.js │ │ ├── decision-tables.js │ │ ├── form-builder.js │ │ ├── form-readonly-view.js │ │ ├── form-toolbar-controller.js │ │ ├── form.js │ │ ├── forms.js │ │ ├── model-common-actions.js │ │ ├── process.js │ │ └── processes.js │ ├── editor-directives.js │ ├── resource-loader.js │ └── services │ │ ├── decision-table-service.js │ │ ├── form-services.js │ │ ├── identity-services.js │ │ └── util-services.js │ └── styles │ ├── common │ ├── bootstrap.min.css │ ├── style-retina.css │ └── style.css │ └── style-editor.css └── test └── java └── com └── example └── demo └── DemoApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | https://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.net.URL; 25 | import java.nio.channels.Channels; 26 | import java.nio.channels.ReadableByteChannel; 27 | import java.util.Properties; 28 | 29 | public class MavenWrapperDownloader { 30 | 31 | /** 32 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 33 | */ 34 | private static final String DEFAULT_DOWNLOAD_URL = 35 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; 36 | 37 | /** 38 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 39 | * use instead of the default one. 40 | */ 41 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 42 | ".mvn/wrapper/maven-wrapper.properties"; 43 | 44 | /** 45 | * Path where the maven-wrapper.jar will be saved to. 46 | */ 47 | private static final String MAVEN_WRAPPER_JAR_PATH = 48 | ".mvn/wrapper/maven-wrapper.jar"; 49 | 50 | /** 51 | * Name of the property which should be used to override the default download url for the wrapper. 52 | */ 53 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 54 | 55 | public static void main(String args[]) { 56 | System.out.println("- Downloader started"); 57 | File baseDirectory = new File(args[0]); 58 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 59 | 60 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 61 | // wrapperUrl parameter. 62 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 63 | String url = DEFAULT_DOWNLOAD_URL; 64 | if (mavenWrapperPropertyFile.exists()) { 65 | FileInputStream mavenWrapperPropertyFileInputStream = null; 66 | try { 67 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 68 | Properties mavenWrapperProperties = new Properties(); 69 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 70 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 71 | } catch (IOException e) { 72 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 73 | } finally { 74 | try { 75 | if (mavenWrapperPropertyFileInputStream != null) { 76 | mavenWrapperPropertyFileInputStream.close(); 77 | } 78 | } catch (IOException e) { 79 | // Ignore ... 80 | } 81 | } 82 | } 83 | System.out.println("- Downloading from: : " + url); 84 | 85 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 86 | if (!outputFile.getParentFile().exists()) { 87 | if (!outputFile.getParentFile().mkdirs()) { 88 | System.out.println( 89 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 90 | } 91 | } 92 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 93 | try { 94 | downloadFileFromURL(url, outputFile); 95 | System.out.println("Done"); 96 | System.exit(0); 97 | } catch (Throwable e) { 98 | System.out.println("- Error downloading"); 99 | e.printStackTrace(); 100 | System.exit(1); 101 | } 102 | } 103 | 104 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 105 | URL website = new URL(urlString); 106 | ReadableByteChannel rbc; 107 | rbc = Channels.newChannel(website.openStream()); 108 | FileOutputStream fos = new FileOutputStream(destination); 109 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 110 | fos.close(); 111 | rbc.close(); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImPYJ/FlowableDemo/c0b2d188d83659b105ccefe3a8910f875a3d3e22/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlowableDemo 2 | SpringBoot + Flowable 实现基础工作流 3 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.0.RELEASE 9 | 10 | 11 | com.example 12 | demo 13 | 0.0.1-SNAPSHOT 14 | demo 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | 28 | org.flowable 29 | flowable-spring-boot-starter 30 | 6.3.0 31 | 32 | 33 | 34 | 35 | mysql 36 | mysql-connector-java 37 | 5.1.45 38 | 39 | 40 | 41 | org.slf4j 42 | slf4j-api 43 | 1.7.21 44 | 45 | 46 | org.slf4j 47 | slf4j-log4j12 48 | 1.7.21 49 | 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-test 55 | test 56 | 57 | 58 | org.junit.vintage 59 | junit-vintage-engine 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-maven-plugin 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/TestController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.flowable.bpmn.model.BpmnModel; 4 | import org.flowable.engine.*; 5 | import org.flowable.engine.history.HistoricActivityInstance; 6 | import org.flowable.engine.history.HistoricProcessInstance; 7 | import org.flowable.engine.runtime.Execution; 8 | import org.flowable.engine.runtime.ProcessInstance; 9 | import org.flowable.image.ProcessDiagramGenerator; 10 | import org.flowable.task.api.Task; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.InputStream; 18 | import java.io.OutputStream; 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | /** 25 | * @author pyj 26 | * @date 2019/10/30 27 | */ 28 | @RestController 29 | @RequestMapping("flowable") 30 | public class TestController { 31 | 32 | @Autowired 33 | private RuntimeService runtimeService; 34 | @Autowired 35 | private TaskService taskService; 36 | @Autowired 37 | private HistoryService historyService; 38 | @Autowired 39 | private RepositoryService repositoryService; 40 | @Autowired 41 | private ProcessEngine processEngine; 42 | 43 | /** 44 | * 创建流程 45 | * 46 | * @param userId 47 | * @param days 48 | * @param reason 49 | * @return 50 | */ 51 | @GetMapping("add") 52 | public String addExpense(String userId, String days, String reason) { 53 | Map map = new HashMap<>(); 54 | map.put("employee", userId); 55 | map.put("nrOfHolidays", days); 56 | map.put("description", reason); 57 | 58 | ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayRequest", map); 59 | return "提交成功,流程ID为:" + processInstance.getId(); 60 | } 61 | 62 | /** 63 | * 获取指定用户组流程任务列表 64 | * 65 | * @param group 66 | * @return 67 | */ 68 | @GetMapping("list") 69 | public Object list(String group) { 70 | List tasks = taskService.createTaskQuery().taskCandidateGroup(group).list(); 71 | return tasks.toString(); 72 | } 73 | 74 | /** 75 | * 通过/拒绝任务 76 | * 77 | * @param taskId 78 | * @param approved 1 :true 2:false 79 | * @return 80 | */ 81 | @GetMapping("apply") 82 | public String apply(String taskId, String approved) { 83 | Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); 84 | if (task == null) { 85 | return "流程不存在"; 86 | } 87 | Map variables = new HashMap<>(); 88 | Boolean apply = approved.equals("1") ? true : false; 89 | variables.put("approved", apply); 90 | taskService.complete(taskId, variables); 91 | return "审批是否通过:" + approved; 92 | 93 | } 94 | 95 | /** 96 | * 查看历史流程记录 97 | * 98 | * @param processInstanceId 99 | * @return 100 | */ 101 | @GetMapping("historyList") 102 | public Object getHistoryList(String processInstanceId) { 103 | List historicActivityInstances = historyService.createHistoricActivityInstanceQuery() 104 | .processInstanceId(processInstanceId).finished().orderByHistoricActivityInstanceEndTime().asc().list(); 105 | 106 | return historicActivityInstances; 107 | } 108 | 109 | /** 110 | * 驳回流程实例 111 | * 112 | * @param taskId 113 | * @param targetTaskKey 114 | * @return 115 | */ 116 | @GetMapping("rollbask") 117 | public String rollbaskTask(String taskId, String targetTaskKey) { 118 | Task currentTask = taskService.createTaskQuery().taskId(taskId).singleResult(); 119 | if (currentTask == null) { 120 | return "节点不存在"; 121 | } 122 | List key = new ArrayList<>(); 123 | key.add(currentTask.getTaskDefinitionKey()); 124 | 125 | 126 | runtimeService.createChangeActivityStateBuilder() 127 | .processInstanceId(currentTask.getProcessInstanceId()) 128 | .moveActivityIdsToSingleActivityId(key, targetTaskKey) 129 | .changeState(); 130 | return "驳回成功..."; 131 | } 132 | 133 | 134 | /** 135 | * 终止流程实例 136 | * 137 | * @param processInstanceId 138 | */ 139 | public String deleteProcessInstanceById(String processInstanceId) { 140 | // ""这个参数本来可以写删除原因 141 | runtimeService.deleteProcessInstance(processInstanceId, ""); 142 | return "终止流程实例成功"; 143 | } 144 | 145 | 146 | /** 147 | * 挂起流程实例 148 | * 149 | * @param processInstanceId 当前流程实例id 150 | */ 151 | @GetMapping("hangUp") 152 | public String handUpProcessInstance(String processInstanceId) { 153 | runtimeService.suspendProcessInstanceById(processInstanceId); 154 | return "挂起流程成功..."; 155 | } 156 | 157 | /** 158 | * 恢复(唤醒)被挂起的流程实例 159 | * 160 | * @param processInstanceId 流程实例id 161 | */ 162 | @GetMapping("recovery") 163 | public String activateProcessInstance(String processInstanceId) { 164 | runtimeService.activateProcessInstanceById(processInstanceId); 165 | return "恢复流程成功..."; 166 | } 167 | 168 | 169 | /** 170 | * 判断传入流程实例在运行中是否存在 171 | * 172 | * @param processInstanceId 173 | * @return 174 | */ 175 | @GetMapping("isExist/running") 176 | public Boolean isExistProcIntRunning(String processInstanceId) { 177 | ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); 178 | if (processInstance == null) { 179 | return false; 180 | } 181 | return true; 182 | } 183 | 184 | /** 185 | * 判断流程实例在历史记录中是否存在 186 | * @param processInstanceId 187 | * @return 188 | */ 189 | @GetMapping("isExist/history") 190 | public Boolean isExistProcInHistory(String processInstanceId) { 191 | HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); 192 | if (historicProcessInstance == null) { 193 | return false; 194 | } 195 | return true; 196 | } 197 | 198 | 199 | /** 200 | * 我发起的流程实例列表 201 | * 202 | * @param userId 203 | * @return 流程实例列表 204 | */ 205 | @GetMapping("myTasks") 206 | public List getMyStartProcint(String userId) { 207 | List list = historyService 208 | .createHistoricProcessInstanceQuery() 209 | .startedBy(userId) 210 | .orderByProcessInstanceStartTime() 211 | .asc() 212 | .list(); 213 | return list; 214 | } 215 | 216 | 217 | /** 218 | * 查询流程图 219 | * 220 | * @param httpServletResponse 221 | * @param processId 222 | * @throws Exception 223 | */ 224 | @RequestMapping(value = "processDiagram") 225 | public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception { 226 | ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult(); 227 | 228 | //流程走完的不显示图 229 | if (pi == null) { 230 | return; 231 | } 232 | Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult(); 233 | //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象 234 | String InstanceId = task.getProcessInstanceId(); 235 | List executions = runtimeService 236 | .createExecutionQuery() 237 | .processInstanceId(InstanceId) 238 | .list(); 239 | 240 | //得到正在执行的Activity的Id 241 | List activityIds = new ArrayList<>(); 242 | List flows = new ArrayList<>(); 243 | for (Execution exe : executions) { 244 | List ids = runtimeService.getActiveActivityIds(exe.getId()); 245 | activityIds.addAll(ids); 246 | } 247 | 248 | //获取流程图 249 | BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId()); 250 | ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration(); 251 | ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator(); 252 | InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0); 253 | OutputStream out = null; 254 | byte[] buf = new byte[1024]; 255 | int legth = 0; 256 | try { 257 | out = httpServletResponse.getOutputStream(); 258 | while ((legth = in.read(buf)) != -1) { 259 | out.write(buf, 0, legth); 260 | } 261 | } finally { 262 | if (in != null) { 263 | in.close(); 264 | } 265 | if (out != null) { 266 | out.close(); 267 | } 268 | } 269 | } 270 | 271 | } 272 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/config/FlowableConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import org.flowable.spring.SpringProcessEngineConfiguration; 4 | import org.flowable.spring.boot.EngineConfigurationConfigurer; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * @author pyj 9 | * 修改连线的名称格式, 10 | * @date 2019/10/30 11 | */ 12 | @Configuration 13 | public class FlowableConfig implements EngineConfigurationConfigurer { 14 | 15 | 16 | @Override 17 | public void configure(SpringProcessEngineConfiguration engineConfiguration) { 18 | engineConfiguration.setActivityFontName("宋体"); 19 | engineConfiguration.setLabelFontName("宋体"); 20 | engineConfiguration.setAnnotationFontName("宋体"); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/controller/IdmController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import org.flowable.idm.api.*; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author pyj 9 | * @date 2019/11/1 10 | */ 11 | public class IdmController implements IdmIdentityService { 12 | @Override 13 | public User newUser(String s) { 14 | return null; 15 | } 16 | 17 | @Override 18 | public void saveUser(User user) { 19 | 20 | } 21 | 22 | @Override 23 | public void updateUserPassword(User user) { 24 | 25 | } 26 | 27 | @Override 28 | public UserQuery createUserQuery() { 29 | return null; 30 | } 31 | 32 | @Override 33 | public NativeUserQuery createNativeUserQuery() { 34 | return null; 35 | } 36 | 37 | @Override 38 | public void deleteUser(String s) { 39 | 40 | } 41 | 42 | @Override 43 | public Group newGroup(String s) { 44 | return null; 45 | } 46 | 47 | @Override 48 | public GroupQuery createGroupQuery() { 49 | return null; 50 | } 51 | 52 | @Override 53 | public NativeGroupQuery createNativeGroupQuery() { 54 | return null; 55 | } 56 | 57 | @Override 58 | public void saveGroup(Group group) { 59 | 60 | } 61 | 62 | @Override 63 | public void deleteGroup(String s) { 64 | 65 | } 66 | 67 | @Override 68 | public void createMembership(String s, String s1) { 69 | 70 | } 71 | 72 | @Override 73 | public void deleteMembership(String s, String s1) { 74 | 75 | } 76 | 77 | @Override 78 | public boolean checkPassword(String s, String s1) { 79 | return false; 80 | } 81 | 82 | @Override 83 | public void setAuthenticatedUserId(String s) { 84 | 85 | } 86 | 87 | @Override 88 | public void setUserPicture(String s, Picture picture) { 89 | 90 | } 91 | 92 | @Override 93 | public Picture getUserPicture(String s) { 94 | return null; 95 | } 96 | 97 | @Override 98 | public Token newToken(String s) { 99 | return null; 100 | } 101 | 102 | @Override 103 | public void saveToken(Token token) { 104 | 105 | } 106 | 107 | @Override 108 | public void deleteToken(String s) { 109 | 110 | } 111 | 112 | @Override 113 | public TokenQuery createTokenQuery() { 114 | return null; 115 | } 116 | 117 | @Override 118 | public NativeTokenQuery createNativeTokenQuery() { 119 | return null; 120 | } 121 | 122 | @Override 123 | public void setUserInfo(String s, String s1, String s2) { 124 | 125 | } 126 | 127 | @Override 128 | public String getUserInfo(String s, String s1) { 129 | return null; 130 | } 131 | 132 | @Override 133 | public List getUserInfoKeys(String s) { 134 | return null; 135 | } 136 | 137 | @Override 138 | public void deleteUserInfo(String s, String s1) { 139 | 140 | } 141 | 142 | @Override 143 | public Privilege createPrivilege(String s) { 144 | return null; 145 | } 146 | 147 | @Override 148 | public void addUserPrivilegeMapping(String s, String s1) { 149 | 150 | } 151 | 152 | @Override 153 | public void deleteUserPrivilegeMapping(String s, String s1) { 154 | 155 | } 156 | 157 | @Override 158 | public void addGroupPrivilegeMapping(String s, String s1) { 159 | 160 | } 161 | 162 | @Override 163 | public void deleteGroupPrivilegeMapping(String s, String s1) { 164 | 165 | } 166 | 167 | @Override 168 | public List getPrivilegeMappingsByPrivilegeId(String s) { 169 | return null; 170 | } 171 | 172 | @Override 173 | public void deletePrivilege(String s) { 174 | 175 | } 176 | 177 | @Override 178 | public List getUsersWithPrivilege(String s) { 179 | return null; 180 | } 181 | 182 | @Override 183 | public List getGroupsWithPrivilege(String s) { 184 | return null; 185 | } 186 | 187 | @Override 188 | public PrivilegeQuery createPrivilegeQuery() { 189 | return null; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/delegate/CallExternalSystemDelegate.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.delegate; 2 | 3 | import org.flowable.engine.delegate.DelegateExecution; 4 | import org.flowable.engine.delegate.JavaDelegate; 5 | 6 | /** 7 | * @author 蒲韵键 8 | * @date 2019/10/29 9 | */ 10 | public class CallExternalSystemDelegate implements JavaDelegate { 11 | public void execute(DelegateExecution delegateExecution) { 12 | System.out.println("Calling the external system for employee " 13 | + delegateExecution.getVariable("employee")); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/delegate/SendRejectionMail.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.delegate; 2 | 3 | import org.flowable.engine.delegate.DelegateExecution; 4 | import org.flowable.engine.delegate.JavaDelegate; 5 | 6 | /** 7 | * @author 蒲韵键 8 | * @date 2019/10/30 9 | */ 10 | public class SendRejectionMail implements JavaDelegate { 11 | @Override 12 | public void execute(DelegateExecution delegateExecution) { 13 | System.out.println("Send Mail " 14 | + delegateExecution.getVariable("employee")); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | spring: 5 | datasource: 6 | url: 7 | username: 8 | password: 9 | driverClassName: com.mysql.jdbc.Driver 10 | resources: 11 | # static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/static/flowable-modeler 12 | flowable: 13 | #关闭定时任务JOB 14 | async-executor-activate: false -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG, CA 2 | 3 | log4j.appender.CA=org.apache.log4j.ConsoleAppender 4 | log4j.appender.CA.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/editor-app/define-data-controller.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | /* 15 | * Controller for morph shape selection 16 | */ 17 | 18 | var FlowableDefineDataCtrl = [ '$rootScope', '$scope', 'dialog', '$timeout', '$translate', function($rootScope, $scope, dialog, $timeout, $translate) { 19 | 20 | $scope.definedDataItems = []; 21 | $scope.selectedDataItems = []; 22 | 23 | // Config for grid 24 | $scope.gridOptions = { 25 | data: 'definedDataItems', 26 | enableRowSelection: true, 27 | headerRowHeight: 28, 28 | multiSelect: false, 29 | keepLastSelected : false, 30 | selectedItems: $scope.selectedDataItems, 31 | enableHorizontalScrollbar: 0, 32 | enableColumnMenus: false, 33 | enableSorting: false, 34 | columnDefs: [{ field: 'name', displayName: 'Name'}, 35 | { field: 'value', displayName: 'Value'}] 36 | }; 37 | 38 | // Click handler for add button 39 | $scope.addNewDataItem = function() { 40 | $scope.definedDataItems.push({ name : '', 41 | value : ''}); 42 | }; 43 | 44 | // Click handler for remove button 45 | $scope.removeDataItem = function() { 46 | if ($scope.selectedDataItems.length > 0) { 47 | var index = $scope.definedDataItems.indexOf($scope.selectedDataItems[0]); 48 | $scope.gridOptions.selectItem(index, false); 49 | $scope.definedDataItems.splice(index, 1); 50 | 51 | $scope.selectedDataItems.length = 0; 52 | if (index < $scope.definedDataItems.length) { 53 | $scope.gridOptions.selectItem(index + 1, true); 54 | } else if ($scope.definedDataItems.length > 0) { 55 | $scope.gridOptions.selectItem(index - 1, true); 56 | } 57 | } 58 | }; 59 | 60 | // Click handler for up button 61 | $scope.moveDataItemUp = function() { 62 | if ($scope.selectedParameters.length > 0) { 63 | var index = $scope.definedDataItems.indexOf($scope.selectedDataItems[0]); 64 | if (index != 0) { // If it's the first, no moving up of course 65 | // Reason for funny way of swapping, see https://github.com/angular-ui/ng-grid/issues/272 66 | var temp = $scope.definedDataItems[index]; 67 | $scope.definedDataItems.splice(index, 1); 68 | $timeout(function(){ 69 | $scope.definedDataItems.splice(index + -1, 0, temp); 70 | }, 100); 71 | 72 | } 73 | } 74 | }; 75 | 76 | // Click handler for down button 77 | $scope.moveDataItemDown = function() { 78 | if ($scope.selectedParameters.length > 0) { 79 | var index = $scope.definedDataItems.indexOf($scope.selectedDataItems[0]); 80 | if (index != $scope.definedDataItems.length - 1) { // If it's the last element, no moving down of course 81 | // Reason for funny way of swapping, see https://github.com/angular-ui/ng-grid/issues/272 82 | var temp = $scope.definedDataItems[index]; 83 | $scope.definedDataItems.splice(index, 1); 84 | $timeout(function(){ 85 | $scope.definedDataItems.splice(index + 1, 0, temp); 86 | }, 100); 87 | 88 | } 89 | } 90 | }; 91 | 92 | $scope.save = function() { 93 | dialog.close(); 94 | }; 95 | 96 | $scope.cancel = function() { 97 | dialog.close(); 98 | }; 99 | 100 | // Close button handler 101 | $scope.close = function() { 102 | dialog.close(); 103 | }; 104 | 105 | }]; -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/editor-app/editor-config.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | 'use strict'; 15 | 16 | var FLOWABLE = FLOWABLE || {}; 17 | 18 | FLOWABLE.UI_CONFIG = { 19 | 'showRemovedProperties' : false 20 | }; 21 | 22 | FLOWABLE.HEADER_CONFIG = { 23 | 'showAppTitle' : true, 24 | 'showHeaderMenu' : true, 25 | 'showMainNavigation' : true, 26 | 'showPageHeader' : true 27 | }; -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/editor-app/eventbus.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | var FLOWABLE = FLOWABLE || {}; 14 | 15 | /** Inspired by https://github.com/krasimir/EventBus/blob/master/src/EventBus.js */ 16 | FLOWABLE.eventBus = { 17 | 18 | /** Event fired when the editor is loaded and ready */ 19 | EVENT_TYPE_EDITOR_READY: 'event-type-editor-ready', 20 | 21 | EVENT_TYPE_EDITOR_BOOTED: 'event-type-editor-booted', 22 | 23 | /** Event fired when a selection is made on the canvas. */ 24 | EVENT_TYPE_SELECTION_CHANGE: 'event-type-selection-change', 25 | 26 | /** Event fired when a toolbar button has been clicked. */ 27 | EVENT_TYPE_TOOLBAR_BUTTON_CLICKED: 'event-type-toolbar-button-clicked', 28 | 29 | /** Event fired when a stencil item is dropped on the canvas. */ 30 | EVENT_TYPE_ITEM_DROPPED: 'event-type-item-dropped', 31 | 32 | /** Event fired when a property value is changed. */ 33 | EVENT_TYPE_PROPERTY_VALUE_CHANGED: 'event-type-property-value-changed', 34 | 35 | /** Event fired on double click in canvas. */ 36 | EVENT_TYPE_DOUBLE_CLICK: 'event-type-double-click', 37 | 38 | /** Event fired on a mouse out */ 39 | EVENT_TYPE_MOUSE_OUT: 'event-type-mouse-out', 40 | 41 | /** Event fired on a mouse over */ 42 | EVENT_TYPE_MOUSE_OVER: 'event-type-mouse-over', 43 | 44 | /** Event fired when a model is saved. */ 45 | EVENT_TYPE_MODEL_SAVED: 'event-type-model-saved', 46 | 47 | /** Event fired when the quick menu buttons should be hidden. */ 48 | EVENT_TYPE_HIDE_SHAPE_BUTTONS: 'event-type-hide-shape-buttons', 49 | 50 | /** Event fired when the validation popup should be shown. */ 51 | EVENT_TYPE_SHOW_VALIDATION_POPUP: 'event-type-show-validation-popup', 52 | 53 | /** Event fired when a different process must be loaded. */ 54 | EVENT_TYPE_NAVIGATE_TO_PROCESS: 'event-type-navigate-to-process', 55 | 56 | EVENT_TYPE_UNDO_REDO_RESET : 'event-type-undo-redo-reset', 57 | 58 | /** A mapping for storing the listeners*/ 59 | listeners: {}, 60 | 61 | /** The Oryx editor, which is stored locally to send events to */ 62 | editor: null, 63 | 64 | /** 65 | * Add an event listener to the event bus, listening to the event with the provided type. 66 | * Type and callback are mandatory parameters. 67 | * 68 | * Provide scope parameter if it is important that the callback is executed 69 | * within a specific scope. 70 | */ 71 | addListener: function (type, callback, scope) { 72 | 73 | // Add to the listeners map 74 | if (typeof this.listeners[type] !== "undefined") { 75 | this.listeners[type].push({scope: scope, callback: callback}); 76 | } else { 77 | this.listeners[type] = [ 78 | {scope: scope, callback: callback} 79 | ]; 80 | } 81 | }, 82 | 83 | /** 84 | * Removes the provided event listener. 85 | */ 86 | removeListener: function (type, callback, scope) { 87 | if (typeof this.listeners[type] != "undefined") { 88 | var numOfCallbacks = this.listeners[type].length; 89 | var newArray = []; 90 | for (var i = 0; i < numOfCallbacks; i++) { 91 | var listener = this.listeners[type][i]; 92 | if (listener.scope === scope && listener.callback === callback) { 93 | // Do nothing, this is the listener and doesn't need to survive 94 | } else { 95 | newArray.push(listener); 96 | } 97 | } 98 | this.listeners[type] = newArray; 99 | } 100 | }, 101 | 102 | hasListener:function(type, callback, scope) { 103 | if(typeof this.listeners[type] != "undefined") { 104 | var numOfCallbacks = this.listeners[type].length; 105 | if(callback === undefined && scope === undefined){ 106 | return numOfCallbacks > 0; 107 | } 108 | for(var i=0; i 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 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 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/editor-app/process-navigator-controller.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | angular.module('flowableModeler').controller('ProcessNavigatorController',['editorManager', '$scope',function(editorManager, $scope){ 15 | //problem here the ORYX editor is bound to the rootscope. In theory this communication should be moved to a service. 16 | 17 | $scope.showSubProcess = function(child){ 18 | var flowableShapes = editorManager.getChildShapeByResourceId(child.resourceId); 19 | editorManager.setSelection([flowableShapes],[],true); 20 | } 21 | 22 | $scope.treeview = {}; 23 | $scope.isEditorReady = false; 24 | 25 | $scope.edit = function(resourceId){ 26 | editorManager.edit(resourceId); 27 | }; 28 | 29 | FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_EDITOR_READY, function(event){ 30 | $scope.isEditorReady = true; 31 | renderProcessHierarchy(); 32 | 33 | editorManager.registerOnEvent(ORYX.CONFIG.ACTION_DELETE_COMPLETED, filterEvent); 34 | 35 | //always a single event. 36 | editorManager.registerOnEvent(ORYX.CONFIG.EVENT_UNDO_ROLLBACK, renderProcessHierarchy); 37 | }) 38 | 39 | //if an element is added te properties will catch this event. 40 | FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED,filterEvent); 41 | FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_ITEM_DROPPED,filterEvent); 42 | FLOWABLE.eventBus.addListener("EDITORMANAGER-EDIT-ACTION",function(){ 43 | renderProcessHierarchy(); 44 | }); 45 | 46 | function filterEvent(event){ 47 | //this event is fired when the user changes a property by the property editor. 48 | if(event.type === "event-type-property-value-changed"){ 49 | if(event.property.key === "oryx-overrideid" || event.property.key === "oryx-name"){ 50 | renderProcessHierarchy() 51 | } 52 | //this event is fired when the stencil / shape's text is changed / updated. 53 | }else if(event.type === "propertyChanged"){ 54 | if(event.name === "oryx-overrideid" || event.name === "oryx-name"){ 55 | renderProcessHierarchy(); 56 | } 57 | }else if(event.type === ORYX.CONFIG.ACTION_DELETE_COMPLETED){ 58 | renderProcessHierarchy(); 59 | //for some reason the new tree does not trigger an ui update. 60 | //$scope.$apply(); 61 | }else if(event.type === "event-type-item-dropped"){ 62 | renderProcessHierarchy(); 63 | } 64 | } 65 | 66 | function renderProcessHierarchy(){ 67 | //only start calculating when the editor has done all his constructor work. 68 | if(!$scope.isEditorReady){ 69 | return false; 70 | } 71 | 72 | if (!editorManager.isLoading()){ 73 | //the current implementation of has a lot of eventlisteners. when calling getTree() it could manipulate 74 | //the canvastracker while the canvas is stille loading stuff. 75 | //TODO: check if its possible to trigger the re-rendering by a single event instead of registering on 10 events... 76 | $scope.treeview = editorManager.getTree(); 77 | } 78 | 79 | } 80 | 81 | }]); 82 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/editor-app/toolbar-controller.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | 'use strict'; 15 | 16 | angular.module('flowableModeler') 17 | .controller('ToolbarController', ['$scope', '$http', '$modal', '$q', '$rootScope', '$translate', '$location', 'editorManager', 18 | function ($scope, $http, $modal, $q, $rootScope, $translate, $location, editorManager) { 19 | 20 | $scope.editorFactory.promise.then(function () { 21 | var toolbarItems = FLOWABLE.TOOLBAR_CONFIG.items; 22 | $scope.items = []; 23 | 24 | for (var i = 0; i < toolbarItems.length; i++) 25 | { 26 | if ($rootScope.modelData.model.modelType === 'form') 27 | { 28 | if (!toolbarItems[i].disableInForm) 29 | { 30 | $scope.items.push(toolbarItems[i]); 31 | } 32 | } 33 | else 34 | { 35 | $scope.items.push(toolbarItems[i]); 36 | } 37 | } 38 | }); 39 | 40 | $scope.secondaryItems = FLOWABLE.TOOLBAR_CONFIG.secondaryItems; 41 | 42 | // Call configurable click handler (From http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string) 43 | var executeFunctionByName = function(functionName, context /*, args */) { 44 | var args = Array.prototype.slice.call(arguments).splice(2); 45 | var namespaces = functionName.split("."); 46 | var func = namespaces.pop(); 47 | for(var i = 0; i < namespaces.length; i++) { 48 | context = context[namespaces[i]]; 49 | } 50 | return context[func].apply(this, args); 51 | }; 52 | 53 | // Click handler for toolbar buttons 54 | $scope.toolbarButtonClicked = function(buttonIndex) { 55 | 56 | // Default behaviour 57 | var buttonClicked = $scope.items[buttonIndex]; 58 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, 'editorManager' : editorManager}; 59 | executeFunctionByName(buttonClicked.action, window, services); 60 | 61 | // Other events 62 | var event = { 63 | type : FLOWABLE.eventBus.EVENT_TYPE_TOOLBAR_BUTTON_CLICKED, 64 | toolbarItem : buttonClicked 65 | }; 66 | FLOWABLE.eventBus.dispatch(event.type, event); 67 | }; 68 | 69 | // Click handler for secondary toolbar buttons 70 | $scope.toolbarSecondaryButtonClicked = function(buttonIndex) { 71 | var buttonClicked = $scope.secondaryItems[buttonIndex]; 72 | var services = { '$scope' : $scope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, '$location': $location, 'editorManager' : editorManager}; 73 | executeFunctionByName(buttonClicked.action, window, services); 74 | }; 75 | 76 | /* Key bindings */ 77 | Mousetrap.bind('mod+z', function(e) { 78 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, 'editorManager' : editorManager}; 79 | FLOWABLE.TOOLBAR.ACTIONS.undo(services); 80 | return false; 81 | }); 82 | 83 | Mousetrap.bind('mod+y', function(e) { 84 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, 'editorManager' : editorManager}; 85 | FLOWABLE.TOOLBAR.ACTIONS.redo(services); 86 | return false; 87 | }); 88 | 89 | Mousetrap.bind('mod+c', function(e) { 90 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, 'editorManager' : editorManager}; 91 | FLOWABLE.TOOLBAR.ACTIONS.copy(services); 92 | return false; 93 | }); 94 | 95 | Mousetrap.bind('mod+v', function(e) { 96 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, 'editorManager' : editorManager}; 97 | FLOWABLE.TOOLBAR.ACTIONS.paste(services); 98 | return false; 99 | }); 100 | 101 | Mousetrap.bind(['del'], function(e) { 102 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, 'editorManager' : editorManager}; 103 | FLOWABLE.TOOLBAR.ACTIONS.deleteItem(services); 104 | return false; 105 | }); 106 | 107 | /* Undo logic */ 108 | 109 | $scope.undoStack = []; 110 | $scope.redoStack = []; 111 | 112 | FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_UNDO_REDO_RESET,function($scope){ 113 | this.undoStack = []; 114 | this.redoStack = []; 115 | if (this.items) { 116 | for(var i = 0; i < this.items.length; i++) { 117 | var item = this.items[i]; 118 | if (item.action === 'FLOWABLE.TOOLBAR.ACTIONS.undo' || item.action === "FLOWABLE.TOOLBAR.ACTIONS.redo"){ 119 | item.enabled = false; 120 | } 121 | } 122 | } 123 | 124 | },$scope); 125 | 126 | $scope.editorFactory.promise.then(function() { 127 | 128 | // Catch all command that are executed and store them on the respective stacks 129 | editorManager.registerOnEvent(ORYX.CONFIG.EVENT_EXECUTE_COMMANDS, function( evt ){ 130 | 131 | // If the event has commands 132 | if( !evt.commands ){ return; } 133 | 134 | $scope.undoStack.push( evt.commands ); 135 | $scope.redoStack = []; 136 | 137 | for(var i = 0; i < $scope.items.length; i++) 138 | { 139 | var item = $scope.items[i]; 140 | if (item.action === 'FLOWABLE.TOOLBAR.ACTIONS.undo') 141 | { 142 | item.enabled = true; 143 | } 144 | else if (item.action === 'FLOWABLE.TOOLBAR.ACTIONS.redo') 145 | { 146 | item.enabled = false; 147 | } 148 | } 149 | 150 | // Update 151 | editorManager.getCanvas().update(); 152 | editorManager.updateSelection(); 153 | 154 | }); 155 | 156 | }); 157 | 158 | // Handle enable/disable toolbar buttons 159 | $scope.editorFactory.promise.then(function() { 160 | editorManager.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function( evt ){ 161 | var elements = evt.elements; 162 | 163 | for(var i = 0; i < $scope.items.length; i++) { 164 | var item = $scope.items[i]; 165 | if (item.enabledAction && item.enabledAction === 'element') { 166 | var minLength = 1; 167 | if (item.minSelectionCount) { 168 | minLength = item.minSelectionCount; 169 | } 170 | 171 | if (elements.length >= minLength && !item.enabled) { 172 | $scope.safeApply(function () { 173 | item.enabled = true; 174 | }); 175 | } else if (elements.length == 0 && item.enabled) { 176 | $scope.safeApply(function () { 177 | item.enabled = false; 178 | }); 179 | } 180 | } 181 | } 182 | }); 183 | 184 | }); 185 | 186 | }]); -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/editor-app/tour.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | var FLOWABLE_EDITOR_TOUR = { 15 | 16 | /* 17 | * General 'getting started' tutorial for the Editor. 18 | */ 19 | gettingStarted: function($scope, $translate, $q, useLocalStorage) { 20 | var userName; 21 | if ($scope.account.firstName) { 22 | userName = $scope.account.firstName; 23 | } else { 24 | userName = $scope.account.fullname; 25 | } 26 | 27 | $q.all([ 28 | $translate('TOUR.WELCOME-TITLE', {userName: userName}), $translate('TOUR.WELCOME-CONTENT'), 29 | $translate('TOUR.PALETTE-TITLE'), $translate('TOUR.PALETTE-CONTENT'), 30 | $translate('TOUR.CANVAS-TITLE'), $translate('TOUR.CANVAS-CONTENT'), 31 | $translate('TOUR.DRAGDROP-TITLE'), $translate('TOUR.DRAGDROP-CONTENT'), 32 | $translate('TOUR.PROPERTIES-TITLE'), $translate('TOUR.PROPERTIES-CONTENT'), 33 | $translate('TOUR.TOOLBAR-TITLE'), $translate('TOUR.TOOLBAR-CONTENT'), 34 | $translate('TOUR.END-TITLE'), $translate('TOUR.END-CONTENT') 35 | ]).then(function(translations) { 36 | 37 | // We're using a hack here due to https://github.com/sorich87/bootstrap-tour/issues/85: 38 | // when clicking next in the tour, it always sets the 'display' css property to 'none' 39 | // The hack is simple: before the next step is shown, we reset the 'display' property to 'block' 40 | 41 | var tourStepDomElements = ['body', '#paletteHelpWrapper', '#canvasHelpWrapper', '#propertiesHelpWrapper', '#editor-header']; 42 | 43 | var tour = new Tour({ 44 | name: 'activitiEditorTour', 45 | storage: (useLocalStorage ? window.localStorage : false), 46 | container: 'body', 47 | backdrop: true, 48 | keyboard: true, 49 | steps: [ 50 | { 51 | orphan: true, 52 | title: translations[0], 53 | content: translations[1], 54 | template: FLOWABLE_EDITOR_TOUR._buildStepTemplate(false, true, false, 300), 55 | onNext: FLOWABLE_EDITOR_TOUR._buildOnNextFunction(tourStepDomElements[0]) 56 | }, 57 | { 58 | element: tourStepDomElements[1], 59 | title: translations[2], 60 | content: translations[3], 61 | template: FLOWABLE_EDITOR_TOUR._buildStepTemplate(false, true, false, 400, 'images/tour/open-group.gif'), 62 | onNext: FLOWABLE_EDITOR_TOUR._buildOnNextFunction(tourStepDomElements[1]) 63 | }, 64 | { 65 | element: tourStepDomElements[2], 66 | title: translations[4], 67 | content: translations[5], 68 | placement: 'left', 69 | template: FLOWABLE_EDITOR_TOUR._buildStepTemplate(false, true, false), 70 | onNext: FLOWABLE_EDITOR_TOUR._buildOnNextFunction(tourStepDomElements[2]) 71 | }, 72 | { 73 | orphan: true, 74 | title: translations[6], 75 | content: translations[7], 76 | template: FLOWABLE_EDITOR_TOUR._buildStepTemplate(false, true, false, 720, 'images/tour/tour-dnd.gif'), 77 | onNext: FLOWABLE_EDITOR_TOUR._buildOnNextFunction(tourStepDomElements[0]) 78 | }, 79 | { 80 | element: tourStepDomElements[3], 81 | title: translations[8], 82 | content: translations[9], 83 | placement: 'top', 84 | template: FLOWABLE_EDITOR_TOUR._buildStepTemplate(false, true, false, 400), 85 | onNext: FLOWABLE_EDITOR_TOUR._buildOnNextFunction(tourStepDomElements[3]) 86 | }, 87 | { 88 | element: tourStepDomElements[4], 89 | title: translations[10], 90 | content: translations[11], 91 | placement: 'bottom', 92 | template: FLOWABLE_EDITOR_TOUR._buildStepTemplate(false, true, false, 400), 93 | onNext: FLOWABLE_EDITOR_TOUR._buildOnNextFunction(tourStepDomElements[4]) 94 | }, 95 | { 96 | orphan: true, 97 | title: translations[12], 98 | content: translations[13], 99 | template: FLOWABLE_EDITOR_TOUR._buildStepTemplate(false, false, true, 400), 100 | onNext: FLOWABLE_EDITOR_TOUR._buildOnNextFunction(tourStepDomElements[0]) 101 | } 102 | ], 103 | 104 | onEnd: FLOWABLE_EDITOR_TOUR._buildOnEndFunction(tourStepDomElements) 105 | }); 106 | 107 | tour.init(); 108 | tour.start(); 109 | }) 110 | }, 111 | 112 | /* 113 | * Tutorial showing how to use the bendpoint functionality for sequenceflow 114 | */ 115 | sequenceFlowBendpoint: function($scope, $translate, $q, useLocalStorage) { 116 | 117 | $q.all([ 118 | $translate('FEATURE-TOUR.BENDPOINT.TITLE'), $translate('FEATURE-TOUR.BENDPOINT.DESCRIPTION') 119 | ]).then(function(translations) { 120 | 121 | // We're using a hack here due to https://github.com/sorich87/bootstrap-tour/issues/85: 122 | // when clicking next in the tour, it always sets the 'display' css property to 'none' 123 | // The hack is simple: before the next step is shown, we reset the 'display' property to 'block' 124 | 125 | var tourStepDomElements = ['body']; 126 | 127 | var tour = new Tour({ 128 | name: 'bendpointTour', 129 | storage: (useLocalStorage ? window.localStorage : false), 130 | container: 'body', 131 | backdrop: true, 132 | keyboard: true, 133 | steps: [ 134 | { 135 | orphan: true, 136 | title: translations[0], 137 | content: translations[1], 138 | template: FLOWABLE_EDITOR_TOUR._buildStepTemplate(false, false, true, 500, 'images/tour/sequenceflow-bendpoint.gif'), 139 | onNext: FLOWABLE_EDITOR_TOUR._buildOnNextFunction(tourStepDomElements[0]) 140 | } 141 | ], 142 | 143 | onEnd: FLOWABLE_EDITOR_TOUR._buildOnEndFunction(tourStepDomElements) 144 | }); 145 | 146 | tour.init(); 147 | tour.start(); 148 | }) 149 | }, 150 | 151 | 152 | 153 | _buildStepTemplate : function (addPrevButton, addNextButton, addEndTourButton, optionalForcedWidth, image) { 154 | 155 | var width = 200; 156 | if (optionalForcedWidth) { 157 | width = optionalForcedWidth; 158 | } 159 | 160 | var template = 161 | '
' + 162 | '
' + 163 | '

' + 164 | '
' + 165 | '
'; 166 | if (image) { 167 | template = template + '
'; 168 | } 169 | if (addPrevButton) { 170 | template = template + ''; 171 | } 172 | if (addNextButton) { 173 | template = template + ''; 174 | } 175 | if (addEndTourButton) { 176 | template = template + ''; 177 | } 178 | 179 | template = template + '
' + '' + '
'; 180 | return template; 181 | }, 182 | 183 | _buildOnNextFunction: function(selector) { 184 | return function () { 185 | jQuery(selector).each(function (i, obj) { 186 | obj.style.display = 'block'; 187 | }) 188 | }; 189 | }, 190 | 191 | _buildOnEndFunction: function(selectors) { 192 | return function () { 193 | for (var elementsToResetIndex = 0; elementsToResetIndex < selectors.length; elementsToResetIndex++) { 194 | jQuery(selectors[elementsToResetIndex]).each(function (i, obj) { 195 | obj.style.display = 'block'; 196 | }); 197 | } 198 | } 199 | } 200 | 201 | }; 202 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/app-cfg.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2005-2015 Alfresco Software, Ltd. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | var FLOWABLE = FLOWABLE || {}; 19 | 20 | var pathname = window.location.pathname.replace(/^(\/[^\/]*)(\/.*)?$/, '$1').replace(/\/$/, ''); 21 | 22 | FLOWABLE.CONFIG = { 23 | 'onPremise' : true, 24 | 'contextRoot' : pathname, 25 | 'webContextRoot' : pathname, 26 | 'datesLocalization' : false 27 | }; 28 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/common/controllers/about.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | flowableApp.controller('AboutFlowablePopupCtrl', ['$rootScope', '$scope', '$http', '$translate', '$interval', '$dateFormatter', function($rootScope, $scope, $http, $translate, $interval, $dateFormatter) { 16 | $scope.popup = { 17 | loading: true, 18 | activitiVersion: {}, 19 | licenseHolder: '' 20 | }; 21 | 22 | $http({method: 'GET', url: FLOWABLE.APP_URL.getAboutInfoUrl()}). 23 | success(function(response, status, headers, config) { 24 | $scope.popup.licenseHolder = response.holder; 25 | $scope.popup.activitiVersion = response.versionInfo.edition + ' v' + response.versionInfo.majorVersion + '.' + response.versionInfo.minorVersion + '.' + response.versionInfo.revisionVersion; 26 | $scope.popup.activitiVersionType = response.versionInfo.type; 27 | $scope.popup.loading = false; 28 | }). 29 | error(function(response, status, headers, config) { 30 | $scope.popup.loading = false; 31 | }); 32 | 33 | 34 | $scope.cancel = function() { 35 | $scope.close(); 36 | }; 37 | 38 | 39 | $scope.close = function() { 40 | $scope.$hide(); 41 | } 42 | 43 | }]); 44 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/common/providers-config.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /** 14 | * Configure the HTTP provider such that no caching happens when fetching 15 | * a resource using $http. 16 | */ 17 | 18 | flowableModule.factory('NotPermittedInterceptor', [ '$q', '$window', function($q, $window) { 19 | return { 20 | responseError: function ( response ) { 21 | 22 | if (response.status === 403) { 23 | $window.location.href = FLOWABLE.CONFIG.contextRoot; 24 | $window.location.reload(); 25 | return $q.reject(response); 26 | } 27 | else{ 28 | return $q.reject(response); 29 | } 30 | } 31 | } 32 | }]); 33 | 34 | flowableModule.config(['$httpProvider', function($httpProvider) { 35 | 36 | if (!$httpProvider.defaults.headers.get) { 37 | $httpProvider.defaults.headers.get = {}; 38 | } 39 | 40 | $httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache, no-store, must-revalidate'; 41 | $httpProvider.defaults.headers.get['Pragma'] = 'no-cache'; 42 | $httpProvider.defaults.headers.get['Expires'] = '0'; 43 | 44 | $httpProvider.interceptors.push('NotPermittedInterceptor'); 45 | 46 | }]); -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/common/services/recursion-helper.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | // Recursion Helper service, makes it possible to use nested directives of the same type 16 | flowableModule.factory('RecursionHelper', ['$compile', function($compile){ 17 | return { 18 | /** 19 | * Manually compiles the element, fixing the recursion loop. 20 | * @param element 21 | * @param [link] A post-link function, or an object with function(s) registered via pre and post properties. 22 | * @returns An object containing the linking functions. 23 | */ 24 | compile: function(element, link){ 25 | // Normalize the link parameter 26 | if(angular.isFunction(link)){ 27 | link = { post: link }; 28 | } 29 | 30 | // Break the recursion loop by removing the contents 31 | var contents = element.contents().remove(); 32 | var compiledContents; 33 | return { 34 | pre: (link && link.pre) ? link.pre : null, 35 | /** 36 | * Compiles and re-adds the contents 37 | */ 38 | post: function(scope, element){ 39 | // Compile the contents 40 | if(!compiledContents){ 41 | compiledContents = $compile(contents); 42 | } 43 | // Re-add the compiled contents to the element 44 | compiledContents(scope, function(clone){ 45 | element.append(clone); 46 | }); 47 | 48 | // Call the post-linking function, if any 49 | if(link && link.post){ 50 | link.post.apply(null, arguments); 51 | } 52 | } 53 | }; 54 | } 55 | }; 56 | }]); -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/common/services/resource-service.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | // User service 16 | flowableModule.service('ResourceService', ['$http', '$q', 'appResourceRoot', 17 | function ($http, $q, appResourceRoot) { 18 | 19 | var loadedResources = {}; 20 | 21 | function loadStylesheet(relativeUrl, cache) 22 | { 23 | var url = appResourceRoot + relativeUrl; 24 | if (!cache || !loadedResources[url]) 25 | { 26 | if (cache) { 27 | loadedResources[url] = true; 28 | } 29 | if (document.createStyleSheet) 30 | { 31 | try 32 | { 33 | document.createStyleSheet(); 34 | } catch (e) { } 35 | } 36 | else { 37 | var link = document.createElement("link"); 38 | link.rel = "stylesheet"; 39 | link.type = "text/css"; 40 | link.media = "all"; 41 | link.href = url; 42 | document.getElementsByTagName("head")[0].appendChild(link); 43 | } 44 | } 45 | } 46 | 47 | function loadScript(relativeUrl, callback, cache) 48 | { 49 | var url = appResourceRoot + relativeUrl; 50 | if (cache && loadedResources[url] && callback) 51 | { 52 | callback(); 53 | } 54 | else 55 | { 56 | if (cache) { 57 | loadedResources[url] = true; 58 | } 59 | 60 | // Insert the node so it gets loaded 61 | var script = document.createElement("script"); 62 | script.type="text/javascript"; 63 | script.src = url; 64 | 65 | if (callback) { 66 | var done = false; 67 | 68 | // Attach handlers for all browsers 69 | script.onload = script.onreadystatechange = function() 70 | { 71 | if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) 72 | { 73 | done = true; 74 | callback(); 75 | } 76 | }; 77 | } 78 | var el = document.getElementsByTagName("head")[0]; 79 | el.appendChild(script); 80 | } 81 | } 82 | 83 | function loadScripts(relativeUrls, callback, cache) 84 | { 85 | function loadNext() 86 | { 87 | var relativeUrl = relativeUrls.shift(); 88 | if (relativeUrl) 89 | { 90 | loadScript.call(this, relativeUrl, loadNext.bind(this), cache); 91 | } 92 | else 93 | { 94 | if (callback) 95 | { 96 | callback(); 97 | } 98 | } 99 | } 100 | loadNext.call(this); 101 | } 102 | 103 | function loadFromHtml(url, callback, cache) 104 | { 105 | $http.get(url).success(function(responseText) 106 | { 107 | var xmlDoc; 108 | if (window.DOMParser) 109 | { 110 | var parser = new DOMParser(); 111 | xmlDoc = parser.parseFromString(responseText, "text/xml"); 112 | } 113 | else // Internet Explorer 114 | { 115 | xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); 116 | xmlDoc.async = false; 117 | xmlDoc.loadXML(responseText); 118 | } 119 | var resources = xmlDoc.getElementsByTagName("link"); 120 | var resourceUrl; 121 | var resourceUrls = []; 122 | for (var i = 0, il = resources.length; i < il; i++) 123 | { 124 | resourceUrl = resources[i].getAttribute("href"); 125 | if (resourceUrl) 126 | { 127 | loadStylesheet(resourceUrl, cache); 128 | } 129 | } 130 | resources = xmlDoc.getElementsByTagName("script"); 131 | for (i = 0, il = resources.length; i < il; i++) 132 | { 133 | resourceUrl = resources[i].getAttribute("src"); 134 | if (resourceUrl) 135 | { 136 | resourceUrls.push(resourceUrl); 137 | } 138 | } 139 | loadScripts(resourceUrls, callback, cache); 140 | }); 141 | } 142 | 143 | this.loadFromHtml = loadFromHtml; 144 | this.loadScript = loadScript; 145 | this.loadStylesheet = loadStylesheet; 146 | }]); 147 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/configuration/app-definition-toolbar-default-actions.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | var APP_DEFINITION_TOOLBAR = { 16 | ACTIONS: { 17 | 18 | saveModel: function (services) { 19 | 20 | _internalCreateModal({ 21 | backdrop: true, 22 | keyboard: true, 23 | template: 'views/popup/app-definition-save-model.html?version=' + Date.now(), 24 | scope: services.$scope 25 | }, services.$modal, services.$scope); 26 | }, 27 | 28 | help: function (services) { 29 | 30 | }, 31 | 32 | feedback: function (services) { 33 | 34 | }, 35 | 36 | closeEditor: function (services) { 37 | services.$location.path('/apps'); 38 | } 39 | } 40 | }; 41 | 42 | /** Custom controller for the save dialog */ 43 | angular.module('flowableModeler').controller('SaveAppDefinitionCtrl', 44 | [ '$rootScope', '$scope', '$http', '$route', '$location', '$translate', 45 | function ($rootScope, $scope, $http, $route, $location, $translate) { 46 | 47 | var description = ''; 48 | if ($rootScope.currentAppDefinition.description) { 49 | description = $rootScope.currentAppDefinition.description; 50 | } 51 | 52 | var saveDialog = { 53 | name: $rootScope.currentAppDefinition.name, 54 | key: $rootScope.currentAppDefinition.key, 55 | description: description, 56 | publish: false 57 | }; 58 | 59 | $scope.saveDialog = saveDialog; 60 | 61 | $scope.status = { 62 | loading: false 63 | }; 64 | 65 | $scope.cancel = function () { 66 | $scope.$hide(); 67 | }; 68 | 69 | $scope.saveAndClose = function (force) { 70 | $scope.save(function() { 71 | $location.path('/apps'); 72 | }, force); 73 | }; 74 | 75 | $scope.save = function (saveCallback, force) { 76 | 77 | if (!$scope.saveDialog.name || $scope.saveDialog.name.length == 0 || 78 | !$scope.saveDialog.key || $scope.saveDialog.key.length == 0) { 79 | 80 | return; 81 | } 82 | 83 | // Indicator spinner image 84 | $scope.status.loading = true; 85 | 86 | var data = { 87 | appDefinition: $rootScope.currentAppDefinition, 88 | publish: $scope.saveDialog.publish 89 | }; 90 | 91 | data.appDefinition.name = $scope.saveDialog.name; 92 | if ($scope.saveDialog.description && $scope.saveDialog.description.length > 0) { 93 | data.appDefinition.description = $scope.saveDialog.description; 94 | } 95 | 96 | if (force !== undefined && force !== null && force === true) { 97 | data.force = true; 98 | } 99 | 100 | delete $scope.conflict; 101 | $http({method: 'PUT', url: FLOWABLE.APP_URL.getAppDefinitionUrl($rootScope.currentAppDefinition.id), data: data}). 102 | success(function(response, status, headers, config) { 103 | // Regular error 104 | if (response.error) { 105 | $scope.status.loading = false; 106 | $scope.saveDialog.errorMessage = response.errorDescription; 107 | } else { 108 | $scope.$hide(); 109 | $rootScope.addAlert($translate.instant('APP.POPUP.SAVE-APP-SAVE-SUCCESS', 'info')); 110 | if (saveCallback) { 111 | saveCallback(); 112 | } 113 | } 114 | 115 | }). 116 | error(function(data, status, headers, config) { 117 | $scope.status.loading = false; 118 | $scope.saveDialog.errorMessage = data.message; 119 | }); 120 | }; 121 | 122 | $scope.isOkButtonDisabled = function() { 123 | if ($scope.status.loading) { 124 | return false; 125 | } else if ($scope.error && $scope.error.hasCustomStencilItem) { 126 | return false; 127 | } 128 | return true; 129 | }; 130 | 131 | $scope.okClicked = function() { 132 | if ($scope.error) { 133 | if ($scope.error.conflictResolveAction === 'discardChanges') { 134 | $scope.close(); 135 | $route.reload(); 136 | } else if ($scope.error.conflictResolveAction === 'overwrite' 137 | || $scope.error.conflictResolveAction === 'newVersion') { 138 | $scope.save(); 139 | } else if($scope.error.conflictResolveAction === 'saveAs') { 140 | $scope.save(function() { 141 | $rootScope.ignoreChanges = true; // Otherwise will get pop up that changes are not saved. 142 | $location.path('/apps'); 143 | }); 144 | } 145 | } 146 | }; 147 | 148 | }]); 149 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/configuration/app-definition-toolbar.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | var APP_DEFINITION_TOOLBAR_CONFIG = { 16 | "items" : [ 17 | { 18 | "type" : "button", 19 | "title" : "APP_DEFINITION_TOOLBAR.ACTION.SAVE", 20 | "cssClass" : "editor-icon editor-icon-save", 21 | "action" : "APP_DEFINITION_TOOLBAR.ACTIONS.saveModel" 22 | } 23 | ], 24 | 25 | "secondaryItems" : [ 26 | { 27 | "type" : "button", 28 | "title" : "Close", 29 | "cssClass" : "glyphicon glyphicon-remove", 30 | "action" : "APP_DEFINITION_TOOLBAR.ACTIONS.closeEditor" 31 | } 32 | ] 33 | }; -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/configuration/decision-table-toolbar-default-actions.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2005-2015 Alfresco Software, Ltd. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 'use strict'; 16 | 17 | var DECISION_TABLE_TOOLBAR = { 18 | ACTIONS: { 19 | 20 | saveModel: function (services) { 21 | 22 | _internalCreateModal({ 23 | backdrop: true, 24 | keyboard: true, 25 | template: 'views/popup/decision-table-save-model.html?version=' + Date.now(), 26 | scope: services.$scope 27 | }, services.$modal, services.$scope); 28 | }, 29 | 30 | help: function (services) { 31 | 32 | }, 33 | 34 | feedback: function (services) { 35 | 36 | }, 37 | 38 | closeEditor: function (services) { 39 | 40 | var callback = function() { 41 | services.$rootScope.decisiontableChanges = false; 42 | 43 | if (services.$rootScope.editorHistory.length > 0) { 44 | var navigationObject = services.$rootScope.editorHistory.pop(); 45 | var additionalParameters = ''; 46 | if (navigationObject.subProcessId && navigationObject.subProcessId.length > 0) { 47 | additionalParameters = '?subProcessId=' + navigationObject.subProcessId; 48 | } 49 | services.$location.url('/editor/' + navigationObject.id + additionalParameters); 50 | } else { 51 | services.$location.path('/decision-tables'); 52 | } 53 | }; 54 | 55 | if (services.$rootScope.decisiontableChanges == true) { 56 | 57 | services.$scope.$emit("decisionTableChangesEvent"); 58 | 59 | var unbindMustSave = services.$scope.$on("mustSaveEvent", function(){ 60 | //save the decision table data 61 | var description = ''; 62 | if (services.$rootScope.currentDecisionTable.description) { 63 | description = services.$rootScope.currentDecisionTable.description; 64 | } 65 | 66 | var data = { 67 | newVersion: false 68 | }; 69 | 70 | unbindEvents(); 71 | services.DecisionTableBuilderService.saveDecisionTable(data, services.$rootScope.currentDecisionTable.name, 72 | null, description, callback); 73 | }); 74 | 75 | var unbindDiscardDataEvent = services.$scope.$on("discardDataEvent", function() { 76 | unbindEvents(); 77 | callback(); 78 | }); 79 | 80 | var unbindContinueEditingEvent = services.$scope.$on("continueEditingEvent", function () { 81 | unbindEvents(); 82 | }); 83 | 84 | } else { 85 | callback(); 86 | } 87 | 88 | var unbindEvents = function () { 89 | unbindContinueEditingEvent(); 90 | unbindMustSave(); 91 | unbindDiscardDataEvent(); 92 | }; 93 | 94 | } 95 | } 96 | }; 97 | 98 | /** Custom controller for the save dialog */ 99 | angular.module('flowableModeler') 100 | .controller('SaveDecisionTableCtrl', [ '$rootScope', '$scope', '$http', '$route', '$location', '$translate', 'DecisionTableService', 'hotRegisterer', 101 | function ($rootScope, $scope, $http, $route, $location, $translate, DecisionTableService, hotRegisterer) { 102 | 103 | var description = ''; 104 | if ($rootScope.currentDecisionTableModel.description) { 105 | description = $rootScope.currentDecisionTableModel.description; 106 | } 107 | 108 | $scope.saveDialog = { 109 | name: $rootScope.currentDecisionTableModel.name, 110 | key: $rootScope.currentDecisionTableModel.key, 111 | description: description, 112 | newVersion: false, 113 | comment: '' 114 | }; 115 | 116 | $scope.keyFieldPattern = /^[a-zA-Z_]\w*$/; 117 | 118 | $scope.status = { 119 | loading: false 120 | }; 121 | 122 | $scope.cancel = function () { 123 | $scope.$hide(); 124 | }; 125 | 126 | $scope.saveAndClose = function () { 127 | $scope.save(function() { 128 | if ($rootScope.editorHistory.length > 0) { 129 | var navigationObject = $rootScope.editorHistory.pop(); 130 | var additionalParameters = ''; 131 | if (navigationObject.subProcessId && navigationObject.subProcessId.length > 0) { 132 | additionalParameters = '?subProcessId=' + navigationObject.subProcessId; 133 | } 134 | $location.url('/editor/' + navigationObject.id + additionalParameters); 135 | 136 | } else { 137 | $location.path('/decision-tables'); 138 | } 139 | }); 140 | }; 141 | 142 | $scope.save = function (additionalSaveCallback) { 143 | 144 | if (!$scope.saveDialog.name || $scope.saveDialog.name.length == 0 || !$scope.saveDialog.key || $scope.saveDialog.key.length == 0) { 145 | return; 146 | } 147 | 148 | // Indicator spinner image 149 | $scope.status = { 150 | loading: true 151 | }; 152 | 153 | var data = { 154 | reusable: $scope.saveDialog.reusable, 155 | newVersion: $scope.saveDialog.newVersion, 156 | comment: $scope.saveDialog.comment 157 | }; 158 | 159 | $rootScope.currentDecisionTableRules = $scope.model.rulesData; 160 | 161 | var saveCallback = function() { 162 | $scope.$hide(); 163 | 164 | $rootScope.currentDecisionTableModel.name = $scope.saveDialog.name; 165 | $rootScope.currentDecisionTableModel.key = $scope.saveDialog.key; 166 | $rootScope.currentDecisionTableModel.description = $scope.saveDialog.description; 167 | 168 | $rootScope.addAlertPromise($translate('DECISION-TABLE-EDITOR.ALERT.SAVE-CONFIRM', {name: $scope.saveDialog.name}), 'info'); 169 | 170 | if (additionalSaveCallback) { 171 | additionalSaveCallback(); 172 | } 173 | 174 | $rootScope.decisionTableChanges = false; 175 | }; 176 | 177 | var errorCallback = function(errorMessage) { 178 | $scope.status.loading = false; 179 | $scope.saveDialog.errorMessage = errorMessage.message; 180 | }; 181 | 182 | // deselect cells before thumbnail generations 183 | var hotDecisionTableEditorInstance = hotRegisterer.getInstance('decision-table-editor'); 184 | if (hotDecisionTableEditorInstance) { 185 | hotDecisionTableEditorInstance.deselectCell(); 186 | } 187 | 188 | DecisionTableService.saveDecisionTable(data, $scope.saveDialog.name, $scope.saveDialog.key, 189 | $scope.saveDialog.description, saveCallback, errorCallback); 190 | }; 191 | 192 | $scope.isOkButtonDisabled = function() { 193 | if ($scope.status.loading) { 194 | return false; 195 | } else if ($scope.error && $scope.error.conflictResolveAction) { 196 | if ($scope.error.conflictResolveAction === 'saveAs') { 197 | return !$scope.error.saveAs || $scope.error.saveAs.length == 0; 198 | } else { 199 | return false; 200 | } 201 | } 202 | return true; 203 | }; 204 | 205 | $scope.okClicked = function() { 206 | if ($scope.error) { 207 | if ($scope.error.conflictResolveAction === 'discardChanges') { 208 | $scope.close(); 209 | $route.reload(); 210 | } else if ($scope.error.conflictResolveAction === 'overwrite' 211 | || $scope.error.conflictResolveAction === 'newVersion') { 212 | $scope.save(); 213 | } else if($scope.error.conflictResolveAction === 'saveAs') { 214 | $scope.save(function() { 215 | $rootScope.ignoreChanges = true; // Otherwise will get pop up that changes are not saved. 216 | $location.path('/decision-tables'); 217 | }); 218 | } 219 | } 220 | }; 221 | 222 | }]); 223 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/configuration/decision-table-toolbar.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2005-2015 Alfresco Software, Ltd. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 'use strict'; 16 | 17 | var DECISION_TABLE_TOOLBAR_CONFIG = { 18 | "items" : [ 19 | { 20 | "type" : "button", 21 | "title" : "TOOLBAR.ACTION.SAVE", 22 | "cssClass" : "editor-icon editor-icon-save", 23 | "action" : "DECISION_TABLE_TOOLBAR.ACTIONS.saveModel", 24 | "disableOnReadonly": true 25 | } 26 | ], 27 | 28 | "secondaryItems" : [ 29 | { 30 | "type" : "button", 31 | "title" : "TOOLBAR.ACTION.CLOSE", 32 | "cssClass" : "glyphicon glyphicon-remove", 33 | "action" : "DECISION_TABLE_TOOLBAR.ACTIONS.closeEditor" 34 | } 35 | ] 36 | }; -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/configuration/form-builder-toolbar-default-actions.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | var FORM_TOOLBAR = { 16 | ACTIONS: { 17 | 18 | saveModel: function (services) { 19 | 20 | _internalCreateModal({ 21 | backdrop: true, 22 | keyboard: true, 23 | template: 'views/popup/form-save-model.html?version=' + Date.now(), 24 | scope: services.$scope 25 | }, services.$modal, services.$scope); 26 | }, 27 | 28 | help: function (services) { 29 | 30 | }, 31 | 32 | closeEditor: function (services) { 33 | if (services.$rootScope.editorHistory.length > 0) { 34 | var callback = function() { 35 | services.$rootScope.formChanges = false; 36 | 37 | var navigationObject = services.$rootScope.editorHistory.pop(); 38 | var additionalParameters = ''; 39 | if (navigationObject.subProcessId && navigationObject.subProcessId.length > 0) { 40 | additionalParameters = '?subProcessId=' + navigationObject.subProcessId; 41 | } 42 | services.$location.url('/editor/' + navigationObject.id + additionalParameters); 43 | }; 44 | 45 | if (services.$rootScope.formChanges == true) { 46 | 47 | services.$scope.$emit("formChangesEvent"); 48 | 49 | var unbindMustSave = services.$scope.$on("mustSaveEvent", function(){ 50 | //save the form data 51 | var description = ''; 52 | if (services.$rootScope.currentForm.description) { 53 | description = services.$rootScope.currentForm.description; 54 | } 55 | 56 | var data = { 57 | newVersion: false 58 | }; 59 | unbindEvents(); 60 | services.FormBuilderService.saveForm(data, services.$rootScope.currentForm.name, description, callback); 61 | }); 62 | 63 | var unbindDiscardDataEvent = services.$scope.$on("discardDataEvent", function() { 64 | unbindEvents(); 65 | callback(); 66 | }); 67 | 68 | var unbindContinueEditingEvent = services.$scope.$on("continueEditingEvent", function () { 69 | unbindEvents(); 70 | }); 71 | 72 | } else { 73 | callback(); 74 | } 75 | 76 | } else { 77 | services.$location.path('/forms'); 78 | } 79 | 80 | var unbindEvents = function () { 81 | unbindContinueEditingEvent(); 82 | unbindMustSave(); 83 | unbindDiscardDataEvent(); 84 | }; 85 | 86 | } 87 | } 88 | }; 89 | 90 | /** Custom controller for the save dialog */ 91 | angular.module('flowableModeler') 92 | .controller('SaveFormCtrl', [ '$rootScope', '$scope', '$http', '$route', '$location', '$translate', 'FormBuilderService', 93 | function ($rootScope, $scope, $http, $route, $location, $translate, FormBuilderService) { 94 | 95 | var formKey = ''; 96 | if ($rootScope.currentForm.key) { 97 | formKey = $rootScope.currentForm.key; 98 | } 99 | 100 | var description = ''; 101 | if ($rootScope.currentForm.description) { 102 | description = $rootScope.currentForm.description; 103 | } 104 | 105 | var saveDialog = { name: $rootScope.currentForm.name, 106 | formKey: formKey, 107 | description: description, 108 | reusable: false, 109 | newVersion: false, 110 | comment: ''}; 111 | 112 | $scope.saveDialog = saveDialog; 113 | 114 | $scope.status = { 115 | loading: false 116 | }; 117 | 118 | $scope.cancel = function () { 119 | $scope.$hide(); 120 | }; 121 | 122 | $scope.saveAndClose = function () { 123 | $scope.save(function() { 124 | if ($rootScope.editorHistory.length > 0) { 125 | var navigationObject = $rootScope.editorHistory.pop(); 126 | var additionalParameters = ''; 127 | if (navigationObject.subProcessId && navigationObject.subProcessId.length > 0) { 128 | additionalParameters = '?subProcessId=' + navigationObject.subProcessId; 129 | } 130 | $location.url('/editor/' + navigationObject.id + additionalParameters); 131 | 132 | } else { 133 | $location.path('/forms'); 134 | } 135 | }); 136 | }; 137 | 138 | $scope.save = function (additionalSaveCallback) { 139 | 140 | if (!$scope.saveDialog.name || $scope.saveDialog.name.length == 0 || 141 | !$scope.saveDialog.formKey || $scope.saveDialog.formKey.length == 0) { 142 | 143 | return; 144 | } 145 | 146 | // Indicator spinner image 147 | $scope.status = { 148 | loading: true 149 | }; 150 | 151 | var data = { 152 | reusable: $scope.saveDialog.reusable, 153 | newVersion: $scope.saveDialog.newVersion, 154 | comment: $scope.saveDialog.comment 155 | }; 156 | 157 | var saveCallback = function() { 158 | $scope.$hide(); 159 | // TODO: i18n 160 | $rootScope.addAlert("Saved form '" + $scope.saveDialog.name, 'info'); 161 | if (additionalSaveCallback) { 162 | additionalSaveCallback(); 163 | } 164 | 165 | $rootScope.formChanges = false; 166 | }; 167 | 168 | var errorCallback = function(errorMessage) { 169 | $scope.status.loading = false; 170 | $scope.saveDialog.errorMessage = errorMessage.message; 171 | }; 172 | 173 | FormBuilderService.saveForm(data, $scope.saveDialog.name, $scope.saveDialog.formKey, 174 | $scope.saveDialog.description, saveCallback, errorCallback); 175 | }; 176 | 177 | $scope.isOkButtonDisabled = function() { 178 | if ($scope.status.loading) { 179 | return false; 180 | } else if ($scope.error && $scope.error.conflictResolveAction) { 181 | if ($scope.error.conflictResolveAction === 'saveAs') { 182 | return !$scope.error.saveAs || $scope.error.saveAs.length == 0; 183 | } else { 184 | return false; 185 | } 186 | } 187 | return true; 188 | }; 189 | 190 | $scope.okClicked = function() { 191 | if ($scope.error) { 192 | if ($scope.error.conflictResolveAction === 'discardChanges') { 193 | $scope.close(); 194 | $route.reload(); 195 | } else if ($scope.error.conflictResolveAction === 'overwrite' 196 | || $scope.error.conflictResolveAction === 'newVersion') { 197 | $scope.save(); 198 | } else if($scope.error.conflictResolveAction === 'saveAs') { 199 | $scope.save(function() { 200 | $rootScope.ignoreChanges = true; // Otherwise will get pop up that changes are not saved. 201 | if ($rootScope.editorHistory.length > 0) { 202 | var navigationObject = $rootScope.editorHistory.pop(); 203 | var additionalParameters = ''; 204 | if (navigationObject.subProcessId && navigationObject.subProcessId.length > 0) { 205 | additionalParameters = '?subProcessId=' + navigationObject.subProcessId; 206 | } 207 | $location.url('/editor/' + navigationObject.id + additionalParameters); 208 | 209 | } else { 210 | $location.path('/forms'); 211 | } 212 | }); 213 | } 214 | } 215 | }; 216 | 217 | }]); 218 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/configuration/form-builder-toolbar.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | var FORM_TOOLBAR_CONFIG = { 16 | "items" : [ 17 | { 18 | "type" : "button", 19 | "title" : "FORM_TOOLBAR.ACTION.SAVE", 20 | "cssClass" : "editor-icon editor-icon-save", 21 | "action" : "FORM_TOOLBAR.ACTIONS.saveModel" 22 | } 23 | ], 24 | 25 | "secondaryItems" : [ 26 | { 27 | "type" : "button", 28 | "title" : "Close", 29 | "cssClass" : "glyphicon glyphicon-remove", 30 | "action" : "FORM_TOOLBAR.ACTIONS.closeEditor" 31 | } 32 | ] 33 | }; -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/configuration/url-config.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | var FLOWABLE = FLOWABLE || {}; 14 | 15 | /* 16 | * Contains methods to retrieve the (mostly) base urls of the different end points. 17 | * Two of the methods #getImageUrl and #getModelThumbnailUrl are exposed in the $rootScope for usage in the HTML views. 18 | */ 19 | FLOWABLE.APP_URL = { 20 | 21 | /* ACCOUNT URLS */ 22 | 23 | getAccountUrl: function () { 24 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/account'; 25 | }, 26 | 27 | getLogoutUrl: function () { 28 | return FLOWABLE.CONFIG.contextRoot + '/app/logout'; 29 | }, 30 | 31 | /* MODEL URLS */ 32 | 33 | getModelsUrl: function (query) { 34 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models' + (query || ""); 35 | }, 36 | 37 | getModelUrl: function (modelId) { 38 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId; 39 | }, 40 | 41 | getModelModelJsonUrl: function (modelId) { 42 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/model-json'; 43 | }, 44 | 45 | getModelBpmn20ExportUrl: function (modelId) { 46 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/bpmn20?version=' + Date.now(); 47 | }, 48 | 49 | getCloneModelsUrl: function (modelId) { 50 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/clone'; 51 | }, 52 | 53 | getModelHistoriesUrl: function (modelId) { 54 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/history'; 55 | }, 56 | 57 | getModelHistoryUrl: function (modelId, modelHistoryId) { 58 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/history/' + modelHistoryId; 59 | }, 60 | 61 | getModelHistoryModelJsonUrl: function (modelId, modelHistoryId) { 62 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/history/' + modelHistoryId + '/model-json'; 63 | }, 64 | 65 | getModelHistoryBpmn20ExportUrl: function (modelId, modelHistoryId) { 66 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/history/' + modelHistoryId + '/bpmn20?version=' + Date.now(); 67 | }, 68 | 69 | getCmmnModelDownloadUrl: function (modelId, modelHistoryId) { 70 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + (modelHistoryId ? '/history/' + modelHistoryId : '') + '/cmmn?version=' + Date.now(); 71 | }, 72 | 73 | getModelParentRelationsUrl: function (modelId) { 74 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/parent-relations'; 75 | }, 76 | 77 | /* APP DEFINITION URLS */ 78 | 79 | getAppDefinitionImportUrl: function (renewIdmIds) { 80 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/import?renewIdmEntries=' + renewIdmIds; 81 | }, 82 | 83 | getAppDefinitionTextImportUrl: function (renewIdmIds) { 84 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/text/import?renewIdmEntries=' + renewIdmIds; 85 | }, 86 | 87 | getAppDefinitionUrl: function (modelId) { 88 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/' + modelId; 89 | }, 90 | 91 | getAppDefinitionModelImportUrl: function (modelId, renewIdmIds) { 92 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/' + modelId + '/import?renewIdmEntries=' + renewIdmIds; 93 | }, 94 | 95 | getAppDefinitionModelTextImportUrl: function (modelId, renewIdmIds) { 96 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/' + modelId + '/text/import?renewIdmEntries=' + renewIdmIds; 97 | }, 98 | 99 | getAppDefinitionPublishUrl: function (modelId) { 100 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/' + modelId + '/publish'; 101 | }, 102 | 103 | getAppDefinitionExportUrl: function (modelId) { 104 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/' + modelId + '/export?version=' + Date.now(); 105 | }, 106 | 107 | getAppDefinitionBarExportUrl: function (modelId) { 108 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/' + modelId + '/export-bar?version=' + Date.now(); 109 | }, 110 | 111 | getAppDefinitionHistoryUrl: function (modelId, historyModelId) { 112 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/app-definitions/' + modelId + '/history/' + historyModelId; 113 | }, 114 | 115 | getModelsForAppDefinitionUrl: function () { 116 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models-for-app-definition'; 117 | }, 118 | 119 | getCmmnModelsForAppDefinitionUrl: function () { 120 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/cmmn-models-for-app-definition'; 121 | }, 122 | 123 | /* PROCESS INSTANCE URLS */ 124 | 125 | getProcessInstanceModelJsonUrl: function (modelId) { 126 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/process-instances/' + modelId + '/model-json'; 127 | }, 128 | 129 | getProcessInstanceModelJsonHistoryUrl: function (historyModelId) { 130 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/process-instances/history/' + historyModelId + '/model-json'; 131 | }, 132 | 133 | /* PROCESS DEFINITION URLS */ 134 | 135 | getProcessDefinitionModelJsonUrl: function (processDefinitionId) { 136 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/process-definitions/' + processDefinitionId + '/model-json'; 137 | }, 138 | 139 | /* PROCESS MODEL URLS */ 140 | 141 | getImportProcessModelUrl: function () { 142 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/import-process-model'; 143 | }, 144 | 145 | getImportProcessModelTextUrl: function () { 146 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/import-process-model/text'; 147 | }, 148 | 149 | /* DECISION TABLE URLS */ 150 | 151 | getDecisionTableModelsUrl: function () { 152 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/decision-table-models'; 153 | }, 154 | 155 | getDecisionTableImportUrl: function () { 156 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/decision-table-models/import-decision-table'; 157 | }, 158 | 159 | getDecisionTableTextImportUrl: function () { 160 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/decision-table-models/import-decision-table-text'; 161 | }, 162 | 163 | getDecisionTableModelUrl: function (modelId) { 164 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/decision-table-models/' + modelId; 165 | }, 166 | 167 | getDecisionTableModelValuesUrl: function (query) { 168 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/decision-table-models/values?' + query; 169 | }, 170 | 171 | getDecisionTableModelsHistoryUrl: function (modelHistoryId) { 172 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/decision-table-models/history/' + modelHistoryId; 173 | }, 174 | 175 | getDecisionTableModelHistoryUrl: function (modelId, modelHistoryId) { 176 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/decision-table-models/' + modelId + '/history/' + modelHistoryId; 177 | }, 178 | 179 | /* FORM MODEL URLS */ 180 | 181 | getFormModelsUrl: function () { 182 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/form-models'; 183 | }, 184 | 185 | getFormModelValuesUrl: function (query) { 186 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/form-models/values?' + query; 187 | }, 188 | 189 | getFormModelUrl: function (modelId) { 190 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/form-models/' + modelId; 191 | }, 192 | 193 | getFormModelHistoryUrl: function (modelId, modelHistoryId) { 194 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/form-models/' + modelId + '/history/' + modelHistoryId; 195 | }, 196 | 197 | /* CASE MODEL URLS */ 198 | 199 | getCaseModelsUrl: function (query) { 200 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/case-models' + (query || ""); 201 | }, 202 | 203 | getCaseModelImportUrl: function () { 204 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/import-case-model'; 205 | }, 206 | 207 | getCaseModelTextImportUrl: function () { 208 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/import-case-model/text'; 209 | }, 210 | 211 | getCaseInstancesHistoryModelJsonUrl: function (modelHistoryId) { 212 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/case-instances/history/' + modelHistoryId + '/model-json'; 213 | }, 214 | 215 | getCaseInstancesModelJsonUrl: function (modelId) { 216 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/case-instances/' + modelId + '/model-json'; 217 | }, 218 | 219 | getCaseDefinitionModelJsonUrl: function (caseDefinitionId) { 220 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/case-definitions/' + caseDefinitionId + '/model-json'; 221 | }, 222 | 223 | /* IMAGE URLS (exposed in rootscope in app.js */ 224 | 225 | getImageUrl: function (imageId) { 226 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/image/' + imageId; 227 | }, 228 | 229 | getModelThumbnailUrl: function (modelId, version) { 230 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/thumbnail' + (version ? "?version=" + version : ""); 231 | }, 232 | 233 | /* OTHER URLS */ 234 | 235 | getEditorUsersUrl: function () { 236 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/editor-users'; 237 | }, 238 | 239 | getEditorGroupsUrl: function () { 240 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/editor-groups'; 241 | }, 242 | 243 | getAboutInfoUrl: function () { 244 | return FLOWABLE.CONFIG.contextRoot + '/app/rest/about-info'; 245 | } 246 | 247 | }; 248 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/app-definition-toolbar-controller.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | angular.module('flowableModeler') 14 | .controller('AppDefinitionToolbarController', ['$scope', '$http', '$modal', '$q', '$rootScope', '$translate', '$location', function ($scope, $http, $modal, $q, $rootScope, $translate, $location) { 15 | 16 | var toolbarItems = APP_DEFINITION_TOOLBAR_CONFIG.items; 17 | $scope.items = []; 18 | 19 | for (var i = 0; i < toolbarItems.length; i++) 20 | { 21 | $scope.items.push(toolbarItems[i]); 22 | } 23 | 24 | $scope.secondaryItems = APP_DEFINITION_TOOLBAR_CONFIG.secondaryItems; 25 | 26 | // Call configurable click handler (From http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string) 27 | var executeFunctionByName = function(functionName, context /*, args */) { 28 | var args = Array.prototype.slice.call(arguments).splice(2); 29 | var namespaces = functionName.split("."); 30 | var func = namespaces.pop(); 31 | for(var i = 0; i < namespaces.length; i++) { 32 | context = context[namespaces[i]]; 33 | } 34 | return context[func].apply(this, args); 35 | }; 36 | 37 | // Click handler for toolbar buttons 38 | $scope.toolbarButtonClicked = function(buttonIndex) { 39 | 40 | // Default behaviour 41 | var buttonClicked = $scope.items[buttonIndex]; 42 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate}; 43 | executeFunctionByName(buttonClicked.action, window, services); 44 | }; 45 | 46 | // Click handler for secondary toolbar buttons 47 | $scope.toolbarSecondaryButtonClicked = function(buttonIndex) { 48 | var buttonClicked = $scope.secondaryItems[buttonIndex]; 49 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, '$location': $location}; 50 | executeFunctionByName(buttonClicked.action, window, services); 51 | }; 52 | }]); -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/app-definitions.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | angular.module('flowableModeler') 16 | .controller('AppDefinitionsCtrl', ['$rootScope', '$scope', '$translate', '$http', '$timeout','$location', '$modal', function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal) { 17 | 18 | // Main page (needed for visual indicator of current page) 19 | $rootScope.setMainPageById('apps'); 20 | 21 | $scope.model = { 22 | filters: [ 23 | {id: 'apps', labelKey: 'APPS'} 24 | ], 25 | 26 | sorts: [ 27 | {id: 'modifiedDesc', labelKey: 'MODIFIED-DESC'}, 28 | {id: 'modifiedAsc', labelKey: 'MODIFIED-ASC'}, 29 | {id: 'nameAsc', labelKey: 'NAME-ASC'}, 30 | {id: 'nameDesc', labelKey: 'NAME-DESC'} 31 | ] 32 | }; 33 | 34 | if ($rootScope.appFilter) { 35 | $scope.model.activeFilter = $rootScope.appFilter.filter; 36 | $scope.model.activeSort = $rootScope.appFilter.sort; 37 | $scope.model.filterText = $rootScope.appFilter.filterText; 38 | 39 | } else { 40 | // By default, show first filter and use first sort 41 | $scope.model.activeFilter = $scope.model.filters[0]; 42 | $scope.model.activeSort = $scope.model.sorts[0]; 43 | $rootScope.appFilter = { 44 | filter: $scope.model.activeFilter, 45 | sort: $scope.model.activeSort, 46 | filterText: '' 47 | }; 48 | } 49 | 50 | $scope.activateFilter = function(filter) { 51 | $scope.model.activeFilter = filter; 52 | $rootScope.appFilter.filter = filter; 53 | $scope.loadApps(); 54 | }; 55 | 56 | $scope.activateSort = function(sort) { 57 | $scope.model.activeSort = sort; 58 | $rootScope.appFilter.sort = sort; 59 | $scope.loadApps(); 60 | }; 61 | 62 | $scope.loadApps = function() { 63 | $scope.model.loading = true; 64 | 65 | var params = { 66 | filter: $scope.model.activeFilter.id, 67 | sort: $scope.model.activeSort.id, 68 | modelType: 3 69 | }; 70 | 71 | if ($scope.model.filterText && $scope.model.filterText != '') { 72 | params.filterText = $scope.model.filterText; 73 | } 74 | 75 | $http({method: 'GET', url: FLOWABLE.APP_URL.getModelsUrl(), params: params}). 76 | success(function(data, status, headers, config) { 77 | $scope.model.apps = data; 78 | $scope.model.loading = false; 79 | }). 80 | error(function(data, status, headers, config) { 81 | $scope.model.loading = false; 82 | }); 83 | }; 84 | 85 | var timeoutFilter = function() { 86 | $scope.model.isFilterDelayed = true; 87 | $timeout(function() { 88 | $scope.model.isFilterDelayed = false; 89 | if($scope.model.isFilterUpdated) { 90 | $scope.model.isFilterUpdated = false; 91 | timeoutFilter(); 92 | } else { 93 | $scope.model.filterText = $scope.model.pendingFilterText; 94 | $rootScope.appFilter.filterText = $scope.model.filterText; 95 | $scope.loadApps(); 96 | } 97 | }, 500); 98 | }; 99 | 100 | $scope.filterDelayed = function() { 101 | if($scope.model.isFilterDelayed) { 102 | $scope.model.isFilterUpdated = true; 103 | } else { 104 | timeoutFilter(); 105 | } 106 | }; 107 | 108 | $scope.createApp = function() { 109 | 110 | _internalCreateModal({ 111 | template: 'views/popup/app-definition-create.html?version=' + Date.now(), 112 | scope: $scope 113 | }, $modal, $scope); 114 | }; 115 | 116 | $scope.showAppDetails = function(app) { 117 | if (app) { 118 | $location.path("/apps/" + app.id); 119 | } 120 | }; 121 | 122 | $scope.editAppDetails = function(app) { 123 | if (app) { 124 | $location.path("/app-editor/" + app.id); 125 | } 126 | }; 127 | 128 | $scope.importAppDefinition = function () { 129 | _internalCreateModal({ 130 | template: 'views/popup/app-definitions-import.html?version=' + Date.now() 131 | }, $modal, $scope); 132 | }; 133 | 134 | // Finally, load initial forms 135 | $scope.loadApps(); 136 | }]); 137 | 138 | 139 | angular.module('flowableModeler') 140 | .controller('CreateNewAppCtrl', ['$rootScope', '$scope', '$http', '$location', '$translate', function ($rootScope, $scope, $http, $location, $translate) { 141 | 142 | $scope.model = { 143 | loading: false, 144 | app: { 145 | name: '', 146 | key: '', 147 | description: '', 148 | modelType: 3 149 | } 150 | }; 151 | 152 | $scope.ok = function () { 153 | 154 | if (!$scope.model.app.name || $scope.model.app.name.length == 0 || 155 | !$scope.model.app.key || $scope.model.app.key.length == 0) { 156 | 157 | return; 158 | } 159 | 160 | $scope.model.loading = true; 161 | 162 | $http({method: 'POST', url: FLOWABLE.APP_URL.getModelsUrl(), data: $scope.model.app}). 163 | success(function (data, status, headers, config) { 164 | $scope.$hide(); 165 | 166 | $scope.model.loading = false; 167 | $location.path("/app-editor/" + data.id); 168 | 169 | }). 170 | error(function (response, status, headers, config) { 171 | $scope.model.loading = false; 172 | 173 | if (response && response.message && response.message.length > 0) { 174 | $scope.model.errorMessage = response.message; 175 | } 176 | }); 177 | }; 178 | 179 | $scope.cancel = function () { 180 | if (!$scope.model.loading) { 181 | $scope.$hide(); 182 | } 183 | }; 184 | }]); 185 | 186 | angular.module('flowableModeler') 187 | .controller('DuplicateAppCtrl', ['$rootScope', '$scope', '$http', '$location', '$translate', function ($rootScope, $scope, $http, $location, $translate) { 188 | 189 | $scope.model = { 190 | loading: false, 191 | app: { 192 | id: '', 193 | name: '', 194 | key: '', 195 | description: '', 196 | modelType: 3 197 | } 198 | }; 199 | 200 | if ($scope.originalModel) { 201 | //clone the model 202 | $scope.model.app.name = $scope.originalModel.app.name; 203 | $scope.model.app.key = $scope.originalModel.app.key; 204 | $scope.model.app.description = $scope.originalModel.app.description; 205 | $scope.model.app.modelType = $scope.originalModel.app.modelType; 206 | $scope.model.app.id = $scope.originalModel.app.id; 207 | } 208 | 209 | $scope.ok = function () { 210 | 211 | if (!$scope.model.app.name || $scope.model.app.name.length == 0) { 212 | return; 213 | } 214 | 215 | $scope.model.loading = true; 216 | 217 | $http({method: 'POST', url: FLOWABLE.APP_URL.getCloneModelsUrl($scope.model.app.id), data: $scope.model.app}). 218 | success(function (data, status, headers, config) { 219 | $scope.$hide(); 220 | 221 | $scope.model.loading = false; 222 | $location.path("/app-editor/" + data.id); 223 | 224 | }). 225 | error(function (response, status, headers, config) { 226 | $scope.model.loading = false; 227 | $scope.model.errorMessage = response.message; 228 | }); 229 | }; 230 | 231 | $scope.cancel = function () { 232 | if (!$scope.model.loading) { 233 | $scope.$hide(); 234 | } 235 | }; 236 | }]); 237 | 238 | angular.module('flowableModeler') 239 | .controller('ImportAppDefinitionCtrl', ['$rootScope', '$scope', '$http', 'Upload', '$location', function ($rootScope, $scope, $http, Upload, $location) { 240 | 241 | $scope.model = { 242 | loading: false, 243 | renewIdmIds: false 244 | }; 245 | 246 | $scope.onFileSelect = function($files, isIE) { 247 | 248 | $scope.model.loading = true; 249 | 250 | for (var i = 0; i < $files.length; i++) { 251 | var file = $files[i]; 252 | 253 | var url; 254 | if (isIE) { 255 | url = FLOWABLE.APP_URL.getAppDefinitionTextImportUrl($scope.model.renewIdmIds); 256 | } else { 257 | url = FLOWABLE.APP_URL.getAppDefinitionImportUrl($scope.model.renewIdmIds); 258 | } 259 | Upload.upload({ 260 | url: url, 261 | method: 'POST', 262 | file: file 263 | }).progress(function(evt) { 264 | $scope.model.uploadProgress = parseInt(100.0 * evt.loaded / evt.total); 265 | 266 | }).success(function(data, status, headers, config) { 267 | $scope.model.loading = false; 268 | 269 | $location.path("/apps/" + data.id); 270 | $scope.$hide(); 271 | 272 | }).error(function(data, status, headers, config) { 273 | 274 | if (data && data.message) { 275 | $scope.model.errorMessage = data.message; 276 | } 277 | 278 | $scope.model.error = true; 279 | $scope.model.loading = false; 280 | }); 281 | } 282 | }; 283 | 284 | $scope.cancel = function () { 285 | if(!$scope.model.loading) { 286 | $scope.$hide(); 287 | } 288 | }; 289 | }]); 290 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/casemodel.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | angular.module('flowableModeler') 14 | .controller('CaseModelCtrl', ['$rootScope', '$scope', '$translate', '$http', '$location', '$routeParams','$modal', '$popover', '$timeout', 'appResourceRoot', 'ResourceService', 15 | function ($rootScope, $scope, $translate, $http, $location, $routeParams, $modal, $popover, $timeout, appResourceRoot, ResourceService) { 16 | 17 | // Main page (needed for visual indicator of current page) 18 | $rootScope.setMainPageById('casemodels'); 19 | 20 | // Initialize model 21 | $scope.model = { 22 | // Store the main model id, this points to the current version of a model, 23 | // even when we're showing history 24 | latestModelId: $routeParams.modelId 25 | }; 26 | 27 | $scope.loadCaseModel = function() { 28 | var url; 29 | if ($routeParams.modelHistoryId) { 30 | url = FLOWABLE.APP_URL.getModelHistoryUrl($routeParams.modelId, $routeParams.modelHistoryId); 31 | } else { 32 | url = FLOWABLE.APP_URL.getModelUrl($routeParams.modelId); 33 | } 34 | 35 | $http({method: 'GET', url: url}). 36 | success(function(data, status, headers, config) { 37 | $scope.model.caseModel = data; 38 | 39 | $scope.loadVersions(); 40 | 41 | $scope.model.cmmnDownloadUrl = FLOWABLE.APP_URL.getCmmnModelDownloadUrl($routeParams.modelId, $routeParams.modelHistoryId); 42 | 43 | 44 | $rootScope.$on('$routeChangeStart', function(event, next, current) { 45 | jQuery('.qtip').qtip('destroy', true); 46 | }); 47 | 48 | $timeout(function() { 49 | jQuery("#cmmnModel").attr('data-model-id', $routeParams.modelId); 50 | jQuery("#cmmnModel").attr('data-model-type', 'design'); 51 | 52 | // in case we want to show a historic model, include additional attribute on the div 53 | if(!$scope.model.caseModel.latestVersion) { 54 | jQuery("#cmmnModel").attr('data-history-id', $routeParams.modelHistoryId); 55 | } 56 | 57 | var viewerUrl = appResourceRoot + "display-cmmn/displaymodel.html?version=" + Date.now(); 58 | 59 | // If Flowable has been deployed inside an AMD environment Raphael will fail to register 60 | // itself globally until displaymodel.js (which depends ona global Raphael variable) is running, 61 | // therefore remove AMD's define method until we have loaded in Raphael and displaymodel.js 62 | // and assume/hope its not used during. 63 | var amdDefine = window.define; 64 | window.define = undefined; 65 | ResourceService.loadFromHtml(viewerUrl, function(){ 66 | // Restore AMD's define method again 67 | window.define = amdDefine; 68 | }); 69 | }); 70 | 71 | }).error(function(data, status, headers, config) { 72 | $scope.returnToList(); 73 | }); 74 | }; 75 | 76 | $scope.useAsNewVersion = function() { 77 | _internalCreateModal({ 78 | template: 'views/popup/model-use-as-new-version.html', 79 | scope: $scope 80 | }, $modal, $scope); 81 | }; 82 | 83 | $scope.loadVersions = function() { 84 | 85 | var params = { 86 | includeLatestVersion: !$scope.model.caseModel.latestVersion 87 | }; 88 | 89 | $http({method: 'GET', url: FLOWABLE.APP_URL.getModelHistoriesUrl($scope.model.latestModelId), params: params}). 90 | success(function(data, status, headers, config) { 91 | if ($scope.model.caseModel.latestVersion) { 92 | if (!data.data) { 93 | data.data = []; 94 | } 95 | data.data.unshift($scope.model.caseModel); 96 | } 97 | 98 | $scope.model.versions = data; 99 | }); 100 | }; 101 | 102 | $scope.showVersion = function(version) { 103 | if(version) { 104 | if(version.latestVersion) { 105 | $location.path("/casemodels/" + $scope.model.latestModelId); 106 | } else{ 107 | // Show latest version, no history-suffix needed in URL 108 | $location.path("/casemodels/" + $scope.model.latestModelId + "/history/" + version.id); 109 | } 110 | } 111 | }; 112 | 113 | $scope.returnToList = function() { 114 | $location.path("/casemodels/"); 115 | }; 116 | 117 | $scope.editCaseModel = function() { 118 | _internalCreateModal({ 119 | template: 'views/popup/model-edit.html', 120 | scope: $scope 121 | }, $modal, $scope); 122 | }; 123 | 124 | $scope.duplicateCaseModel = function() { 125 | var modalInstance = _internalCreateModal({ 126 | template: 'views/popup/casemodel-duplicate.html?version=' + Date.now() 127 | }, $modal, $scope); 128 | 129 | modalInstance.$scope.originalModel = $scope.model; 130 | }; 131 | 132 | $scope.deleteCaseModel = function() { 133 | _internalCreateModal({ 134 | template: 'views/popup/model-delete.html', 135 | scope: $scope 136 | }, $modal, $scope); 137 | }; 138 | 139 | $scope.openEditor = function() { 140 | if ($scope.model.caseModel) { 141 | $location.path("/editor/" + $scope.model.caseModel.id); 142 | } 143 | }; 144 | 145 | $scope.toggleHistory = function($event) { 146 | if(!$scope.historyState) { 147 | var state = {}; 148 | $scope.historyState = state; 149 | 150 | // Create popover 151 | state.popover = $popover(angular.element($event.target), { 152 | template: 'views/popover/history.html', 153 | placement: 'bottom-right', 154 | show: true, 155 | scope: $scope, 156 | container: 'body' 157 | }); 158 | 159 | var destroy = function() { 160 | state.popover.destroy(); 161 | delete $scope.historyState; 162 | } 163 | 164 | // When popup is hidden or scope is destroyed, hide popup 165 | state.popover.$scope.$on('tooltip.hide', destroy); 166 | $scope.$on('$destroy', destroy); 167 | } 168 | }; 169 | 170 | $scope.loadCaseModel(); 171 | }]); 172 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/casemodels.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | angular.module('flowableModeler') 16 | .controller('CaseModelsCtrl', ['$rootScope', '$scope', '$translate', '$http', '$timeout','$location', '$modal', function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal) { 17 | 18 | // Main page (needed for visual indicator of current page) 19 | $rootScope.setMainPageById('casemodels'); 20 | $rootScope.formItems = undefined; 21 | 22 | // get latest thumbnails 23 | $scope.imageVersion = Date.now(); 24 | 25 | $scope.model = { 26 | filters: [ 27 | {id: 'cases', labelKey: 'CASES'} 28 | ], 29 | 30 | sorts: [ 31 | {id: 'modifiedDesc', labelKey: 'MODIFIED-DESC'}, 32 | {id: 'modifiedAsc', labelKey: 'MODIFIED-ASC'}, 33 | {id: 'nameAsc', labelKey: 'NAME-ASC'}, 34 | {id: 'nameDesc', labelKey: 'NAME-DESC'} 35 | ] 36 | }; 37 | 38 | // By default, show first filter and use first sort 39 | $scope.model.activeFilter = $scope.model.filters[0]; 40 | $scope.model.activeSort = $scope.model.sorts[0]; 41 | 42 | $scope.activateFilter = function(filter) { 43 | $scope.model.activeFilter = filter; 44 | $rootScope.modelFilter.filter = filter; 45 | $scope.loadCaseModels(); 46 | }; 47 | 48 | $scope.activateSort = function(sort) { 49 | $scope.model.activeSort = sort; 50 | $rootScope.modelFilter.sort = sort; 51 | $scope.loadCaseModels(); 52 | }; 53 | 54 | $scope.loadCaseModels = function() { 55 | $scope.model.loading = true; 56 | 57 | var params = { 58 | filter: $scope.model.activeFilter.id, 59 | sort: $scope.model.activeSort.id, 60 | modelType: 5 61 | }; 62 | 63 | if ($scope.model.filterText && $scope.model.filterText != '') { 64 | params.filterText = $scope.model.filterText; 65 | } 66 | 67 | $http({method: 'GET', url: FLOWABLE.APP_URL.getModelsUrl(), params: params}). 68 | success(function(data, status, headers, config) { 69 | $scope.model.caseModels = data; 70 | $scope.model.loading = false; 71 | }). 72 | error(function(data, status, headers, config) { 73 | console.log('Something went wrong: ' + data); 74 | $scope.model.loading = false; 75 | }); 76 | }; 77 | 78 | var timeoutFilter = function() { 79 | $scope.model.isFilterDelayed = true; 80 | $timeout(function() { 81 | $scope.model.isFilterDelayed = false; 82 | if ($scope.model.isFilterUpdated) { 83 | $scope.model.isFilterUpdated = false; 84 | timeoutFilter(); 85 | } else { 86 | $scope.model.filterText = $scope.model.pendingFilterText; 87 | $rootScope.modelFilter.filterText = $scope.model.filterText; 88 | $scope.loadCaseModels(); 89 | } 90 | }, 500); 91 | }; 92 | 93 | $scope.filterDelayed = function() { 94 | if ($scope.model.isFilterDelayed) { 95 | $scope.model.isFilterUpdated = true; 96 | } else { 97 | timeoutFilter(); 98 | } 99 | }; 100 | 101 | $scope.createCaseModel = function(mode) { 102 | var modalInstance = _internalCreateModal({ 103 | template: 'views/popup/casemodel-create.html?version=' + Date.now() 104 | }, $modal, $scope); 105 | }; 106 | 107 | $scope.importCaseModel = function () { 108 | _internalCreateModal({ 109 | template: 'views/popup/casemodel-import.html?version=' + Date.now() 110 | }, $modal, $scope); 111 | }; 112 | 113 | $scope.showCaseModelDetails = function(caseModel) { 114 | if (caseModel) { 115 | $rootScope.editorHistory = []; 116 | $location.path("/casemodels/" + caseModel.id); 117 | } 118 | }; 119 | 120 | $scope.editCaseModelDetails = function(caseModel) { 121 | if (caseModel) { 122 | $rootScope.editorHistory = []; 123 | $location.path("/editor/" + caseModel.id); 124 | } 125 | }; 126 | 127 | // Finally, load initial cases 128 | $scope.loadCaseModels(); 129 | }]); 130 | 131 | angular.module('flowableModeler') 132 | .controller('CreateNewCaseModelModelCtrl', ['$rootScope', '$scope', '$modal', '$http', '$location', 133 | function ($rootScope, $scope, $modal, $http, $location) { 134 | 135 | $scope.model = { 136 | loading: false, 137 | caseModel: { 138 | name: '', 139 | key: '', 140 | description: '', 141 | modelType: 5 142 | } 143 | }; 144 | 145 | if ($scope.initialModelType !== undefined) { 146 | $scope.model.caseModel.modelType = $scope.initialModelType; 147 | } 148 | 149 | $scope.ok = function () { 150 | 151 | if (!$scope.model.caseModel.name || $scope.model.caseModel.name.length == 0 || 152 | !$scope.model.caseModel.key || $scope.model.caseModel.key.length == 0) { 153 | 154 | return; 155 | } 156 | 157 | $scope.model.loading = true; 158 | 159 | $http({method: 'POST', url: FLOWABLE.APP_URL.getModelsUrl(), data: $scope.model.caseModel}). 160 | success(function(data) { 161 | $scope.$hide(); 162 | 163 | $scope.model.loading = false; 164 | $rootScope.editorHistory = []; 165 | $location.path("/case-editor/" + data.id); 166 | }). 167 | error(function(data, status, headers, config) { 168 | $scope.model.loading = false; 169 | $scope.model.errorMessage = data.message; 170 | }); 171 | }; 172 | 173 | $scope.cancel = function () { 174 | if(!$scope.model.loading) { 175 | $scope.$hide(); 176 | } 177 | }; 178 | }]); 179 | 180 | angular.module('flowableModeler') 181 | .controller('DuplicateCaseModelCtrl', ['$rootScope', '$scope', '$modal', '$http', '$location', 182 | function ($rootScope, $scope, $modal, $http, $location) { 183 | 184 | $scope.model = { 185 | loading: false, 186 | caseModel: { 187 | name: '', 188 | key: '', 189 | description: '' 190 | } 191 | }; 192 | 193 | if ($scope.originalModel) { 194 | //clone the model 195 | $scope.model.caseModel.name = $scope.originalModel.caseModel.name; 196 | $scope.model.caseModel.key = $scope.originalModel.caseModel.key; 197 | $scope.model.caseModel.description = $scope.originalModel.caseModel.description; 198 | $scope.model.caseModel.id = $scope.originalModel.caseModel.id; 199 | $scope.model.caseModel.modelType = $scope.originalModel.caseModel.modelType; 200 | } 201 | 202 | $scope.ok = function () { 203 | 204 | if (!$scope.model.caseModel.name || $scope.model.caseModel.name.length == 0 || 205 | !$scope.model.caseModel.key || $scope.model.caseModel.key.length == 0) { 206 | 207 | return; 208 | } 209 | 210 | $scope.model.loading = true; 211 | 212 | $http({method: 'POST', url: FLOWABLE.APP_URL.getCloneModelsUrl($scope.model.caseModel.id), data: $scope.model.caseModel}). 213 | success(function(data) { 214 | $scope.$hide(); 215 | 216 | $scope.model.loading = false; 217 | $rootScope.editorHistory = []; 218 | $location.path("/editor/" + data.id); 219 | }). 220 | error(function(data, status, headers, config) { 221 | $scope.model.loading = false; 222 | $scope.model.errorMessage = data.message; 223 | }); 224 | }; 225 | 226 | $scope.cancel = function () { 227 | if(!$scope.model.loading) { 228 | $scope.$hide(); 229 | } 230 | }; 231 | }]); 232 | 233 | angular.module('flowableModeler') 234 | .controller('ImportCaseModelCtrl', ['$rootScope', '$scope', '$http', 'Upload', '$location', function ($rootScope, $scope, $http, Upload, $location) { 235 | 236 | $scope.model = { 237 | loading: false 238 | }; 239 | 240 | $scope.onFileSelect = function($files, isIE) { 241 | 242 | $scope.model.loading = true; 243 | 244 | for (var i = 0; i < $files.length; i++) { 245 | var file = $files[i]; 246 | 247 | var url; 248 | if (isIE) { 249 | url = FLOWABLE.APP_URL.getCaseModelTextImportUrl(); 250 | } else { 251 | url = FLOWABLE.APP_URL.getCaseModelImportUrl(); 252 | } 253 | 254 | Upload.upload({ 255 | url: url, 256 | method: 'POST', 257 | file: file 258 | }).progress(function(evt) { 259 | $scope.model.uploadProgress = parseInt(100.0 * evt.loaded / evt.total); 260 | 261 | }).success(function(data) { 262 | $scope.model.loading = false; 263 | 264 | $location.path("/editor/" + data.id); 265 | $scope.$hide(); 266 | 267 | }).error(function(data) { 268 | 269 | if (data && data.message) { 270 | $scope.model.errorMessage = data.message; 271 | } 272 | 273 | $scope.model.error = true; 274 | $scope.model.loading = false; 275 | }); 276 | } 277 | }; 278 | 279 | $scope.cancel = function () { 280 | if(!$scope.model.loading) { 281 | $scope.$hide(); 282 | } 283 | }; 284 | }]); 285 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/decision-table-toolbar-controller.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2005-2015 Alfresco Software, Ltd. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | angular.module('flowableModeler') 16 | .controller('DecisionTableToolbarController', ['$scope', '$http', '$modal', '$q', '$rootScope', '$translate', '$location', 'DecisionTableService', 17 | function ($scope, $http, $modal, $q, $rootScope, $translate, $location, DecisionTableService) { 18 | 19 | var toolbarItems = DECISION_TABLE_TOOLBAR_CONFIG.items; 20 | $scope.items = []; 21 | 22 | for (var i = 0; i < toolbarItems.length; i++) 23 | { 24 | $scope.items.push(toolbarItems[i]); 25 | } 26 | 27 | $scope.secondaryItems = DECISION_TABLE_TOOLBAR_CONFIG.secondaryItems; 28 | 29 | // Call configurable click handler (From http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string) 30 | var executeFunctionByName = function(functionName, context /*, args */) { 31 | var args = Array.prototype.slice.call(arguments).splice(2); 32 | var namespaces = functionName.split("."); 33 | var func = namespaces.pop(); 34 | for(var i = 0; i < namespaces.length; i++) { 35 | context = context[namespaces[i]]; 36 | } 37 | return context[func].apply(this, args); 38 | }; 39 | 40 | // Click handler for toolbar buttons 41 | $scope.toolbarButtonClicked = function(buttonIndex) { 42 | 43 | // Default behaviour 44 | var buttonClicked = $scope.items[buttonIndex]; 45 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, 'DecisionTableService': DecisionTableService}; 46 | executeFunctionByName(buttonClicked.action, window, services); 47 | }; 48 | 49 | // Click handler for secondary toolbar buttons 50 | $scope.toolbarSecondaryButtonClicked = function(buttonIndex) { 51 | var buttonClicked = $scope.secondaryItems[buttonIndex]; 52 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, '$location': $location, 'DecisionTableService': DecisionTableService }; 53 | executeFunctionByName(buttonClicked.action, window, services); 54 | }; 55 | }]); -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/form-readonly-view.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | angular.module('flowableModeler') 16 | .controller('FormReadonlyViewController', ['$rootScope', '$scope', '$translate', '$http', '$timeout', '$location', '$modal', '$routeParams', '$popover', 17 | function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal, $routeParams, $popover) { 18 | 19 | // Main page (needed for visual indicator of current page) 20 | $rootScope.setMainPageById('forms'); 21 | 22 | var guidSequence = 0; 23 | 24 | function setFieldDragDropAttributes (field, prefix) { 25 | if (!field._guid) { 26 | field._guid = prefix + guidSequence++; 27 | } 28 | 29 | if (!field._width) { 30 | field._width = 1; 31 | } 32 | } 33 | 34 | if ($routeParams.modelId) { 35 | 36 | var url; 37 | if ($routeParams.modelHistoryId) { 38 | url = FLOWABLE.APP_URL.getFormModelHistoryUrl($routeParams.modelId,$routeParams.modelHistoryId); 39 | } else { 40 | url = FLOWABLE.APP_URL.getFormModelUrl($routeParams.modelId); 41 | } 42 | 43 | $http({method: 'GET', url: url}). 44 | success(function (response, status, headers, config) { 45 | if (response.formDefinition.fields) { 46 | for (var i = 0; i < response.formDefinition.fields.length; i++) { 47 | var field = response.formDefinition.fields[i]; 48 | if (!field.params) { 49 | field.params = {}; 50 | } 51 | setFieldDragDropAttributes(field, 'savedField'); 52 | } 53 | 54 | $scope.formElements = response.formDefinition.fields; 55 | } else { 56 | $scope.formElements = []; 57 | } 58 | 59 | $scope.formItems = $scope.formElements; 60 | 61 | $timeout(function () { 62 | // Flip switch in timeout to start watching all form-related models 63 | // after next digest cycle, to prevent first false-positive 64 | $scope.formLoaded = true; 65 | }, 200); 66 | }). 67 | error(function (response, status, headers, config) { 68 | $scope.model.loading = false; 69 | }); 70 | 71 | } else { 72 | $scope.formLoaded = true; 73 | } 74 | 75 | }]); 76 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/form-toolbar-controller.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | angular.module('flowableModeler') 14 | .controller('FormToolbarController', ['$scope', '$http', '$modal', '$q', '$rootScope', '$translate', '$location', 'FormBuilderService', function ($scope, $http, $modal, $q, $rootScope, $translate, $location, FormBuilderService) { 15 | 16 | var toolbarItems = FORM_TOOLBAR_CONFIG.items; 17 | $scope.items = []; 18 | 19 | for (var i = 0; i < toolbarItems.length; i++) 20 | { 21 | $scope.items.push(toolbarItems[i]); 22 | } 23 | 24 | $scope.secondaryItems = FORM_TOOLBAR_CONFIG.secondaryItems; 25 | 26 | // Call configurable click handler (From http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string) 27 | var executeFunctionByName = function(functionName, context /*, args */) { 28 | var args = Array.prototype.slice.call(arguments).splice(2); 29 | var namespaces = functionName.split("."); 30 | var func = namespaces.pop(); 31 | for(var i = 0; i < namespaces.length; i++) { 32 | context = context[namespaces[i]]; 33 | } 34 | return context[func].apply(this, args); 35 | }; 36 | 37 | // Click handler for toolbar buttons 38 | $scope.toolbarButtonClicked = function(buttonIndex) { 39 | 40 | // Default behaviour 41 | var buttonClicked = $scope.items[buttonIndex]; 42 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, 'FormBuilderService': FormBuilderService}; 43 | executeFunctionByName(buttonClicked.action, window, services); 44 | }; 45 | 46 | // Click handler for secondary toolbar buttons 47 | $scope.toolbarSecondaryButtonClicked = function(buttonIndex) { 48 | var buttonClicked = $scope.secondaryItems[buttonIndex]; 49 | var services = { '$scope' : $scope, '$rootScope' : $rootScope, '$http' : $http, '$modal' : $modal, '$q' : $q, '$translate' : $translate, '$location': $location, 'FormBuilderService': FormBuilderService}; 50 | executeFunctionByName(buttonClicked.action, window, services); 51 | }; 52 | }]); -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/form.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | angular.module('flowableModeler') 14 | .controller('FormCtrl', ['$rootScope', '$scope', '$translate', '$http', '$location', '$routeParams','$modal', '$timeout', '$popover', 15 | function ($rootScope, $scope, $translate, $http, $location, $routeParams, $modal, $timeout, $popover) { 16 | 17 | // Main page (needed for visual indicator of current page) 18 | $rootScope.setMainPageById('forms'); 19 | 20 | $scope.formMode = 'read'; 21 | 22 | // Initialize model 23 | $scope.model = { 24 | // Store the main model id, this points to the current version of a model, 25 | // even when we're showing history 26 | latestModelId: $routeParams.modelId 27 | }; 28 | 29 | $scope.loadForm = function() { 30 | var url; 31 | if ($routeParams.modelHistoryId) { 32 | url = FLOWABLE.APP_URL.getModelHistoryUrl($routeParams.modelId, $routeParams.modelHistoryId); 33 | } else { 34 | url = FLOWABLE.APP_URL.getModelUrl($routeParams.modelId); 35 | } 36 | 37 | $http({method: 'GET', url: url}). 38 | success(function(data, status, headers, config) { 39 | $scope.model.form = data; 40 | $scope.loadVersions(); 41 | 42 | }).error(function(data, status, headers, config) { 43 | $scope.returnToList(); 44 | }); 45 | }; 46 | 47 | $scope.useAsNewVersion = function() { 48 | _internalCreateModal({ 49 | template: 'views/popup/model-use-as-new-version.html', 50 | scope: $scope 51 | }, $modal, $scope); 52 | }; 53 | 54 | $scope.loadVersions = function() { 55 | 56 | var params = { 57 | includeLatestVersion: !$scope.model.form.latestVersion 58 | }; 59 | 60 | $http({method: 'GET', url: FLOWABLE.APP_URL.getModelHistoriesUrl($scope.model.latestModelId), params: params}). 61 | success(function(data, status, headers, config) { 62 | if ($scope.model.form.latestVersion) { 63 | if (!data.data) { 64 | data.data = []; 65 | } 66 | data.data.unshift($scope.model.form); 67 | } 68 | 69 | $scope.model.versions = data; 70 | }); 71 | }; 72 | 73 | $scope.showVersion = function(version) { 74 | if (version) { 75 | if (version.latestVersion) { 76 | $location.path("/forms/" + $scope.model.latestModelId); 77 | } else { 78 | // Show latest version, no history-suffix needed in URL 79 | $location.path("/forms/" + $scope.model.latestModelId + "/history/" + version.id); 80 | } 81 | } 82 | }; 83 | 84 | $scope.returnToList = function() { 85 | $location.path("/forms/"); 86 | }; 87 | 88 | $scope.editForm = function() { 89 | _internalCreateModal({ 90 | template: 'views/popup/model-edit.html', 91 | scope: $scope 92 | }, $modal, $scope); 93 | }; 94 | 95 | $scope.duplicateForm = function() { 96 | 97 | var modalInstance = _internalCreateModal({ 98 | template: 'views/popup/form-duplicate.html?version=' + Date.now() 99 | }, $modal, $scope); 100 | 101 | modalInstance.$scope.originalModel = $scope.model; 102 | 103 | modalInstance.$scope.duplicateFormCallback = function(result) { 104 | $rootScope.editorHistory = []; 105 | $location.path("/form-editor/" + result.id); 106 | }; 107 | }; 108 | 109 | $scope.deleteForm = function() { 110 | _internalCreateModal({ 111 | template: 'views/popup/model-delete.html', 112 | scope: $scope 113 | }, $modal, $scope); 114 | }; 115 | 116 | $scope.openEditor = function() { 117 | if ($scope.model.form) { 118 | $location.path("/form-editor/" + $scope.model.form.id); 119 | } 120 | }; 121 | 122 | $scope.toggleHistory = function($event) { 123 | if (!$scope.historyState) { 124 | var state = {}; 125 | $scope.historyState = state; 126 | 127 | // Create popover 128 | state.popover = $popover(angular.element($event.target), { 129 | template: 'views/popover/history.html', 130 | placement: 'bottom-right', 131 | show: true, 132 | scope: $scope, 133 | container: 'body' 134 | }); 135 | 136 | var destroy = function() { 137 | state.popover.destroy(); 138 | delete $scope.historyState; 139 | }; 140 | 141 | // When popup is hidden or scope is destroyed, hide popup 142 | state.popover.$scope.$on('tooltip.hide', destroy); 143 | $scope.$on('$destroy', destroy); 144 | } 145 | }; 146 | 147 | $scope.loadForm(); 148 | 149 | }]); 150 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/forms.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | angular.module('flowableModeler') 16 | .controller('FormsCtrl', ['$rootScope', '$scope', '$translate', '$http', '$timeout','$location', '$modal', function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal) { 17 | 18 | // Main page (needed for visual indicator of current page) 19 | $rootScope.setMainPageById('forms'); 20 | $rootScope.formItems = undefined; 21 | 22 | // get latest thumbnails 23 | $scope.imageVersion = Date.now(); 24 | 25 | $scope.model = { 26 | filters: [ 27 | {id: 'forms', labelKey: 'FORMS'} 28 | ], 29 | 30 | sorts: [ 31 | {id: 'modifiedDesc', labelKey: 'MODIFIED-DESC'}, 32 | {id: 'modifiedAsc', labelKey: 'MODIFIED-ASC'}, 33 | {id: 'nameAsc', labelKey: 'NAME-ASC'}, 34 | {id: 'nameDesc', labelKey: 'NAME-DESC'} 35 | ] 36 | }; 37 | 38 | if ($rootScope.formFilter) { 39 | $scope.model.activeFilter = $rootScope.formFilter.filter; 40 | $scope.model.activeSort = $rootScope.formFilter.sort; 41 | $scope.model.filterText = $rootScope.formFilter.filterText; 42 | 43 | } else { 44 | // By default, show first filter and use first sort 45 | $scope.model.activeFilter = $scope.model.filters[0]; 46 | $scope.model.activeSort = $scope.model.sorts[0]; 47 | $rootScope.formFilter = { 48 | filter: $scope.model.activeFilter, 49 | sort: $scope.model.activeSort, 50 | filterText: '' 51 | }; 52 | } 53 | 54 | $scope.activateFilter = function(filter) { 55 | $scope.model.activeFilter = filter; 56 | $rootScope.formFilter.filter = filter; 57 | $scope.loadForms(); 58 | }; 59 | 60 | $scope.activateSort = function(sort) { 61 | $scope.model.activeSort = sort; 62 | $rootScope.formFilter.sort = sort; 63 | $scope.loadForms(); 64 | }; 65 | 66 | $scope.loadForms = function() { 67 | $scope.model.loading = true; 68 | 69 | var params = { 70 | filter: $scope.model.activeFilter.id, 71 | sort: $scope.model.activeSort.id, 72 | modelType: 2 73 | }; 74 | 75 | if ($scope.model.filterText && $scope.model.filterText != '') { 76 | params.filterText = $scope.model.filterText; 77 | } 78 | 79 | $http({method: 'GET', url: FLOWABLE.APP_URL.getModelsUrl(), params: params}). 80 | success(function(data, status, headers, config) { 81 | $scope.model.forms = data; 82 | $scope.model.loading = false; 83 | }). 84 | error(function(data, status, headers, config) { 85 | $scope.model.loading = false; 86 | }); 87 | }; 88 | 89 | var timeoutFilter = function() { 90 | $scope.model.isFilterDelayed = true; 91 | $timeout(function() { 92 | $scope.model.isFilterDelayed = false; 93 | if($scope.model.isFilterUpdated) { 94 | $scope.model.isFilterUpdated = false; 95 | timeoutFilter(); 96 | } else { 97 | $scope.model.filterText = $scope.model.pendingFilterText; 98 | $rootScope.formFilter.filterText = $scope.model.filterText; 99 | $scope.loadForms(); 100 | } 101 | }, 500); 102 | }; 103 | 104 | $scope.filterDelayed = function() { 105 | if($scope.model.isFilterDelayed) { 106 | $scope.model.isFilterUpdated = true; 107 | } else { 108 | timeoutFilter(); 109 | } 110 | }; 111 | 112 | $scope.createForm = function() { 113 | $rootScope.currentKickstartModel = undefined; 114 | $scope.createFormCallback = function(result) { 115 | $rootScope.editorHistory = []; 116 | $location.path("/form-editor/" + result.id); 117 | }; 118 | _internalCreateModal({ 119 | template: 'views/popup/form-create.html?version=' + Date.now(), 120 | scope: $scope 121 | }, $modal, $scope); 122 | }; 123 | 124 | $scope.showFormDetails = function(form) { 125 | if (form) { 126 | $rootScope.editorHistory = []; 127 | $location.path("/forms/" + form.id); 128 | } 129 | }; 130 | 131 | $scope.editFormDetails = function(form) { 132 | if (form) { 133 | $rootScope.editorHistory = []; 134 | $location.path("/form-editor/" + form.id); 135 | } 136 | }; 137 | 138 | // Finally, load initial forms 139 | $scope.loadForms(); 140 | }]); 141 | 142 | 143 | angular.module('flowableModeler') 144 | .controller('CreateNewFormCtrl', ['$rootScope', '$scope', '$http', 145 | function ($rootScope, $scope, $http) { 146 | 147 | $scope.model = { 148 | loading: false, 149 | form: { 150 | name: '', 151 | key: '', 152 | description: '', 153 | modelType: 2 154 | } 155 | }; 156 | 157 | $scope.ok = function () { 158 | 159 | if (!$scope.model.form.name || $scope.model.form.name.length == 0 || 160 | !$scope.model.form.key || $scope.model.form.key.length == 0) { 161 | 162 | return; 163 | } 164 | 165 | $scope.model.loading = true; 166 | 167 | $http({method: 'POST', url: FLOWABLE.APP_URL.getModelsUrl(), data: $scope.model.form}). 168 | success(function(data, status, headers, config) { 169 | $scope.$hide(); 170 | $scope.model.loading = false; 171 | 172 | if ($scope.createFormCallback) { 173 | $scope.createFormCallback(data); 174 | $scope.createFormCallback = undefined; 175 | } 176 | 177 | }). 178 | error(function(data, status, headers, config) { 179 | $scope.model.loading = false; 180 | $scope.model.errorMessage = data.message; 181 | }); 182 | }; 183 | 184 | $scope.cancel = function () { 185 | if(!$scope.model.loading) { 186 | $scope.$hide(); 187 | } 188 | }; 189 | }]); 190 | 191 | angular.module('flowableModeler') 192 | .controller('DuplicateFormCtrl', ['$rootScope', '$scope', '$http', 193 | function ($rootScope, $scope, $http) { 194 | 195 | $scope.model = { 196 | loading: false, 197 | form: { 198 | id: '', 199 | name: '', 200 | key: '', 201 | description: '', 202 | modelType: 2 203 | } 204 | }; 205 | 206 | if ($scope.originalModel) { 207 | //clone the model 208 | $scope.model.form.name = $scope.originalModel.form.name; 209 | $scope.model.form.key = $scope.originalModel.form.key; 210 | $scope.model.form.description = $scope.originalModel.form.description; 211 | $scope.model.form.modelType = $scope.originalModel.form.modelType; 212 | $scope.model.form.id = $scope.originalModel.form.id; 213 | } 214 | 215 | $scope.ok = function () { 216 | 217 | if (!$scope.model.form.name || $scope.model.form.name.length == 0 || 218 | !$scope.model.form.key || $scope.model.form.key.length == 0) { 219 | 220 | return; 221 | } 222 | 223 | $scope.model.loading = true; 224 | 225 | $http({method: 'POST', url: FLOWABLE.APP_URL.getCloneModelsUrl($scope.model.form.id), data: $scope.model.form}). 226 | success(function(data, status, headers, config) { 227 | $scope.$hide(); 228 | $scope.model.loading = false; 229 | 230 | if ($scope.duplicateFormCallback) { 231 | $scope.duplicateFormCallback(data); 232 | $scope.duplicateFormCallback = undefined; 233 | } 234 | 235 | }). 236 | error(function(data, status, headers, config) { 237 | $scope.model.loading = false; 238 | $scope.model.errorMessage = data.message; 239 | }); 240 | }; 241 | 242 | $scope.cancel = function () { 243 | if(!$scope.model.loading) { 244 | $scope.$hide(); 245 | } 246 | }; 247 | }]); 248 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/model-common-actions.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | angular.module('flowableModeler') 16 | .controller('EditModelPopupCtrl', ['$rootScope', '$scope', '$http', '$translate', '$location', 17 | function ($rootScope, $scope, $http, $translate, $location) { 18 | 19 | var model; 20 | var popupType; 21 | if ($scope.model.process) { 22 | model = $scope.model.process; 23 | popupType = 'PROCESS'; 24 | } else if ($scope.model.caseModel) { 25 | model = $scope.model.caseModel; 26 | popupType = 'CASE'; 27 | } else if ($scope.model.form) { 28 | model = $scope.model.form; 29 | popupType = 'FORM'; 30 | } else if ($scope.model.decisionTable) { 31 | model = $scope.model.decisionTable; 32 | popupType = 'DECISION-TABLE'; 33 | } else { 34 | model = $scope.model.app; 35 | popupType = 'APP'; 36 | } 37 | 38 | $scope.popup = { 39 | loading: false, 40 | popupType: popupType, 41 | modelName: model.name, 42 | modelKey: model.key, 43 | modelDescription: model.description, 44 | id: model.id 45 | }; 46 | 47 | $scope.ok = function () { 48 | 49 | if (!$scope.popup.modelName || $scope.popup.modelName.length == 0 || 50 | !$scope.popup.modelKey || $scope.popup.modelKey.length == 0) { 51 | 52 | return; 53 | } 54 | 55 | $scope.model.name = $scope.popup.modelName; 56 | $scope.model.key = $scope.popup.modelKey; 57 | $scope.model.description = $scope.popup.modelDescription; 58 | 59 | $scope.popup.loading = true; 60 | var updateData = { 61 | name: $scope.model.name, 62 | key: $scope.model.key, description: 63 | $scope.model.description 64 | }; 65 | 66 | $http({method: 'PUT', url: FLOWABLE.APP_URL.getModelUrl($scope.popup.id), data: updateData}). 67 | success(function(data, status, headers, config) { 68 | if ($scope.model.process) { 69 | $scope.model.process = data; 70 | } else if ($scope.model.caseModel) { 71 | $scope.model.caseModel = data; 72 | } else if ($scope.model.form) { 73 | $scope.model.form = data; 74 | } else if ($scope.model.decisionTable) { 75 | $scope.model.decisionTable = data; 76 | } else { 77 | $scope.model.app = data; 78 | } 79 | 80 | $scope.addAlertPromise($translate('PROCESS.ALERT.EDIT-CONFIRM'), 'info'); 81 | $scope.$hide(); 82 | $scope.popup.loading = false; 83 | 84 | if (popupType === 'FORM') { 85 | $location.path("/forms/" + $scope.popup.id); 86 | } else if (popupType === 'APP') { 87 | $location.path("/apps/" + $scope.popup.id); 88 | } else if (popupType === 'DECISION-TABLE') { 89 | $location.path("/decision-tables/" + $scope.popup.id); 90 | } else if (popupType === 'CASE') { 91 | $location.path("/casemodels/" + $scope.popup.id); 92 | } else { 93 | $location.path("/processes/" + $scope.popup.id); 94 | } 95 | 96 | }). 97 | error(function(data, status, headers, config) { 98 | $scope.popup.loading = false; 99 | $scope.popup.errorMessage = data.message; 100 | }); 101 | }; 102 | 103 | $scope.cancel = function () { 104 | if (!$scope.popup.loading) { 105 | $scope.$hide(); 106 | } 107 | }; 108 | }]); 109 | 110 | angular.module('flowableModeler') 111 | .controller('DeleteModelPopupCtrl', ['$rootScope', '$scope', '$http', '$translate', function ($rootScope, $scope, $http, $translate) { 112 | 113 | var model; 114 | var popupType; 115 | if ($scope.model.process) { 116 | model = $scope.model.process; 117 | popupType = 'PROCESS'; 118 | } else if ($scope.model.caseModel) { 119 | model = $scope.model.caseModel; 120 | popupType = 'CASE'; 121 | } else if ($scope.model.form) { 122 | model = $scope.model.form; 123 | popupType = 'FORM'; 124 | } else if ($scope.model.decisionTable) { 125 | model = $scope.model.decisionTable; 126 | popupType = 'DECISION-TABLE'; 127 | } else { 128 | model = $scope.model.app; 129 | popupType = 'APP'; 130 | } 131 | 132 | $scope.popup = { 133 | loading: true, 134 | loadingRelations: true, 135 | cascade: 'false', 136 | popupType: popupType, 137 | model: model 138 | }; 139 | 140 | // Loading relations when opening 141 | $http({method: 'GET', url: FLOWABLE.APP_URL.getModelParentRelationsUrl($scope.popup.model.id)}). 142 | success(function (data, status, headers, config) { 143 | $scope.popup.loading = false; 144 | $scope.popup.loadingRelations = false; 145 | $scope.popup.relations = data; 146 | }). 147 | error(function (data, status, headers, config) { 148 | $scope.$hide(); 149 | $scope.popup.loading = false; 150 | }); 151 | 152 | $scope.ok = function () { 153 | $scope.popup.loading = true; 154 | var params = { 155 | // Explicit string-check because radio-values cannot be js-booleans 156 | cascade: $scope.popup.cascade === 'true' 157 | }; 158 | 159 | $http({method: 'DELETE', url: FLOWABLE.APP_URL.getModelUrl($scope.popup.model.id), params: params}). 160 | success(function (data, status, headers, config) { 161 | $scope.$hide(); 162 | $scope.popup.loading = false; 163 | $scope.addAlertPromise($translate(popupType + '.ALERT.DELETE-CONFIRM'), 'info'); 164 | $scope.returnToList(); 165 | }). 166 | error(function (data, status, headers, config) { 167 | $scope.$hide(); 168 | $scope.popup.loading = false; 169 | }); 170 | }; 171 | 172 | $scope.cancel = function () { 173 | if (!$scope.popup.loading) { 174 | $scope.$hide(); 175 | } 176 | }; 177 | }]); 178 | 179 | angular.module('flowableModeler') 180 | .controller('UseAsNewVersionPopupCtrl', ['$rootScope', '$scope', '$http', '$translate', '$location', function ($rootScope, $scope, $http, $translate, $location) { 181 | 182 | var model; 183 | var popupType; 184 | if ($scope.model.process) { 185 | model = $scope.model.process; 186 | popupType = 'PROCESS'; 187 | } else if ($scope.model.caseModel) { 188 | model = $scope.model.caseModel; 189 | popupType = 'CASE'; 190 | } else if ($scope.model.form) { 191 | model = $scope.model.form; 192 | popupType = 'FORM'; 193 | } else if ($scope.model.decisionTable) { 194 | model = $scope.model.decisionTable; 195 | popupType = 'DECISION-TABLE'; 196 | } else { 197 | model = $scope.model.app; 198 | popupType = 'APP'; 199 | } 200 | 201 | $scope.popup = { 202 | loading: false, 203 | model: model, 204 | popupType: popupType, 205 | latestModelId: $scope.model.latestModelId, 206 | comment: '' 207 | }; 208 | 209 | $scope.ok = function () { 210 | $scope.popup.loading = true; 211 | 212 | var actionData = { 213 | action: 'useAsNewVersion', 214 | comment: $scope.popup.comment 215 | }; 216 | 217 | $http({method: 'POST', url: FLOWABLE.APP_URL.getModelHistoryUrl($scope.popup.latestModelId, $scope.popup.model.id), data: actionData}). 218 | success(function(data, status, headers, config) { 219 | 220 | var backToOverview = function() { 221 | if (popupType === 'FORM') { 222 | $location.path("/forms/" + $scope.popup.latestModelId); 223 | } else if (popupType === 'APP') { 224 | $location.path("/apps/" + $scope.popup.latestModelId); 225 | } else if (popupType === 'DECISION-TABLE') { 226 | $location.path("/decision-tables/" + $scope.popup.latestModelId); 227 | } else if (popupType === 'CASE') { 228 | $location.path("/casemodels/" + $scope.popup.latestModelId); 229 | } else { 230 | $location.path("/processes/" + $scope.popup.latestModelId); 231 | } 232 | }; 233 | 234 | 235 | if (data && data.unresolvedModels && data.unresolvedModels.length > 0) { 236 | 237 | // There were unresolved models 238 | 239 | $scope.popup.loading = false; 240 | $scope.popup.foundUnresolvedModels = true; 241 | $scope.popup.unresolvedModels = data.unresolvedModels; 242 | 243 | $scope.close = function() { 244 | $scope.$hide(); 245 | backToOverview(); 246 | }; 247 | 248 | 249 | } else { 250 | 251 | // All models working resolved perfectly 252 | 253 | $scope.popup.loading = false; 254 | $scope.$hide(); 255 | 256 | $scope.addAlertPromise($translate(popupType + '.ALERT.NEW-VERSION-CONFIRM'), 'info'); 257 | backToOverview(); 258 | 259 | } 260 | 261 | }). 262 | error(function(data, status, headers, config) { 263 | $scope.$hide(); 264 | $scope.popup.loading = false; 265 | }); 266 | }; 267 | 268 | $scope.cancel = function () { 269 | if (!$scope.popup.loading) { 270 | $scope.$hide(); 271 | } 272 | }; 273 | }]); 274 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/process.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | angular.module('flowableModeler') 14 | .controller('ProcessCtrl', ['$rootScope', '$scope', '$translate', '$http', '$location', '$routeParams','$modal', '$popover', '$timeout', 'appResourceRoot', 'ResourceService', 15 | function ($rootScope, $scope, $translate, $http, $location, $routeParams, $modal, $popover, $timeout, appResourceRoot, ResourceService) { 16 | 17 | // Main page (needed for visual indicator of current page) 18 | $rootScope.setMainPageById('processes'); 19 | 20 | // Initialize model 21 | $scope.model = { 22 | // Store the main model id, this points to the current version of a model, 23 | // even when we're showing history 24 | latestModelId: $routeParams.modelId 25 | }; 26 | 27 | $scope.loadProcess = function() { 28 | var url; 29 | if ($routeParams.modelHistoryId) { 30 | url = FLOWABLE.APP_URL.getModelUrl($routeParams.modelId) + '/history/' + $routeParams.modelHistoryId; 31 | } else { 32 | url = FLOWABLE.APP_URL.getModelUrl($routeParams.modelId); 33 | } 34 | 35 | $http({method: 'GET', url: url}). 36 | success(function(data, status, headers, config) { 37 | $scope.model.process = data; 38 | 39 | $scope.loadVersions(); 40 | 41 | $scope.model.bpmn20DownloadUrl = $routeParams.modelHistoryId == undefined ? 42 | FLOWABLE.APP_URL.getModelBpmn20ExportUrl($routeParams.modelId) : 43 | FLOWABLE.APP_URL.getModelHistoryBpmn20ExportUrl($routeParams.modelId, $routeParams.modelHistoryId); 44 | 45 | 46 | $rootScope.$on('$routeChangeStart', function(event, next, current) { 47 | jQuery('.qtip').qtip('destroy', true); 48 | }); 49 | 50 | $timeout(function() { 51 | jQuery("#bpmnModel").attr('data-model-id', $routeParams.modelId); 52 | jQuery("#bpmnModel").attr('data-model-type', 'design'); 53 | 54 | // in case we want to show a historic model, include additional attribute on the div 55 | if(!$scope.model.process.latestVersion) { 56 | jQuery("#bpmnModel").attr('data-history-id', $routeParams.modelHistoryId); 57 | } 58 | 59 | var viewerUrl = appResourceRoot + "display/displaymodel.html?version=" + Date.now(); 60 | 61 | // If Flowable has been deployed inside an AMD environment Raphael will fail to register 62 | // itself globally until displaymodel.js (which depends ona global Raphale variable) is running, 63 | // therefore remove AMD's define method until we have loaded in Raphael and displaymodel.js 64 | // and assume/hope its not used during. 65 | var amdDefine = window.define; 66 | window.define = undefined; 67 | ResourceService.loadFromHtml(viewerUrl, function(){ 68 | // Restore AMD's define method again 69 | window.define = amdDefine; 70 | }); 71 | }); 72 | 73 | }).error(function(data, status, headers, config) { 74 | $scope.returnToList(); 75 | }); 76 | }; 77 | 78 | $scope.useAsNewVersion = function() { 79 | _internalCreateModal({ 80 | template: 'views/popup/model-use-as-new-version.html', 81 | scope: $scope 82 | }, $modal, $scope); 83 | }; 84 | 85 | $scope.loadVersions = function() { 86 | 87 | var params = { 88 | includeLatestVersion: !$scope.model.process.latestVersion 89 | }; 90 | 91 | $http({method: 'GET', url: FLOWABLE.APP_URL.getModelHistoriesUrl($scope.model.latestModelId), params: params}). 92 | success(function(data, status, headers, config) { 93 | if ($scope.model.process.latestVersion) { 94 | if (!data.data) { 95 | data.data = []; 96 | } 97 | data.data.unshift($scope.model.process); 98 | } 99 | 100 | $scope.model.versions = data; 101 | }); 102 | }; 103 | 104 | $scope.showVersion = function(version) { 105 | if(version) { 106 | if(version.latestVersion) { 107 | $location.path("/processes/" + $scope.model.latestModelId); 108 | } else{ 109 | // Show latest version, no history-suffix needed in URL 110 | $location.path("/processes/" + $scope.model.latestModelId + "/history/" + version.id); 111 | } 112 | } 113 | }; 114 | 115 | $scope.returnToList = function() { 116 | $location.path("/processes/"); 117 | }; 118 | 119 | $scope.editProcess = function() { 120 | _internalCreateModal({ 121 | template: 'views/popup/model-edit.html', 122 | scope: $scope 123 | }, $modal, $scope); 124 | }; 125 | 126 | $scope.duplicateProcess = function() { 127 | var modalInstance = _internalCreateModal({ 128 | template: 'views/popup/process-duplicate.html?version=' + Date.now() 129 | }, $modal, $scope); 130 | 131 | modalInstance.$scope.originalModel = $scope.model; 132 | }; 133 | 134 | $scope.deleteProcess = function() { 135 | _internalCreateModal({ 136 | template: 'views/popup/model-delete.html', 137 | scope: $scope 138 | }, $modal, $scope); 139 | }; 140 | 141 | $scope.openEditor = function() { 142 | if ($scope.model.process) { 143 | $location.path("/editor/" + $scope.model.process.id); 144 | } 145 | }; 146 | 147 | $scope.toggleHistory = function($event) { 148 | if(!$scope.historyState) { 149 | var state = {}; 150 | $scope.historyState = state; 151 | 152 | // Create popover 153 | state.popover = $popover(angular.element($event.target), { 154 | template: 'views/popover/history.html', 155 | placement: 'bottom-right', 156 | show: true, 157 | scope: $scope, 158 | container: 'body' 159 | }); 160 | 161 | var destroy = function() { 162 | state.popover.destroy(); 163 | delete $scope.historyState; 164 | } 165 | 166 | // When popup is hidden or scope is destroyed, hide popup 167 | state.popover.$scope.$on('tooltip.hide', destroy); 168 | $scope.$on('$destroy', destroy); 169 | } 170 | }; 171 | 172 | $scope.loadProcess(); 173 | }]); 174 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/controllers/processes.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | angular.module('flowableModeler') 16 | .controller('ProcessesCtrl', ['$rootScope', '$scope', '$translate', '$http', '$timeout','$location', '$modal', function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal) { 17 | 18 | // Main page (needed for visual indicator of current page) 19 | $rootScope.setMainPageById('processes'); 20 | $rootScope.formItems = undefined; 21 | 22 | // get latest thumbnails 23 | $scope.imageVersion = Date.now(); 24 | 25 | $scope.model = { 26 | filters: [ 27 | {id: 'processes', labelKey: 'PROCESSES'} 28 | ], 29 | 30 | sorts: [ 31 | {id: 'modifiedDesc', labelKey: 'MODIFIED-DESC'}, 32 | {id: 'modifiedAsc', labelKey: 'MODIFIED-ASC'}, 33 | {id: 'nameAsc', labelKey: 'NAME-ASC'}, 34 | {id: 'nameDesc', labelKey: 'NAME-DESC'} 35 | ] 36 | }; 37 | 38 | if ($rootScope.modelFilter) { 39 | $scope.model.activeFilter = $rootScope.modelFilter.filter; 40 | $scope.model.activeSort = $rootScope.modelFilter.sort; 41 | $scope.model.filterText = $rootScope.modelFilter.filterText; 42 | 43 | } else { 44 | // By default, show first filter and use first sort 45 | $scope.model.activeFilter = $scope.model.filters[0]; 46 | $scope.model.activeSort = $scope.model.sorts[0]; 47 | $rootScope.modelFilter = { 48 | filter: $scope.model.activeFilter, 49 | sort: $scope.model.activeSort, 50 | filterText: '' 51 | }; 52 | } 53 | 54 | $scope.activateFilter = function(filter) { 55 | $scope.model.activeFilter = filter; 56 | $rootScope.modelFilter.filter = filter; 57 | $scope.loadProcesses(); 58 | }; 59 | 60 | $scope.activateSort = function(sort) { 61 | $scope.model.activeSort = sort; 62 | $rootScope.modelFilter.sort = sort; 63 | $scope.loadProcesses(); 64 | }; 65 | 66 | $scope.loadProcesses = function() { 67 | $scope.model.loading = true; 68 | 69 | var params = { 70 | filter: $scope.model.activeFilter.id, 71 | sort: $scope.model.activeSort.id, 72 | modelType: 0 73 | }; 74 | 75 | if ($scope.model.filterText && $scope.model.filterText != '') { 76 | params.filterText = $scope.model.filterText; 77 | } 78 | 79 | $http({method: 'GET', url: FLOWABLE.APP_URL.getModelsUrl(), params: params}). 80 | success(function(data, status, headers, config) { 81 | $scope.model.processes = data; 82 | $scope.model.loading = false; 83 | }). 84 | error(function(data, status, headers, config) { 85 | console.log('Something went wrong: ' + data); 86 | $scope.model.loading = false; 87 | }); 88 | }; 89 | 90 | var timeoutFilter = function() { 91 | $scope.model.isFilterDelayed = true; 92 | $timeout(function() { 93 | $scope.model.isFilterDelayed = false; 94 | if ($scope.model.isFilterUpdated) { 95 | $scope.model.isFilterUpdated = false; 96 | timeoutFilter(); 97 | } else { 98 | $scope.model.filterText = $scope.model.pendingFilterText; 99 | $rootScope.modelFilter.filterText = $scope.model.filterText; 100 | $scope.loadProcesses(); 101 | } 102 | }, 500); 103 | }; 104 | 105 | $scope.filterDelayed = function() { 106 | if ($scope.model.isFilterDelayed) { 107 | $scope.model.isFilterUpdated = true; 108 | } else { 109 | timeoutFilter(); 110 | } 111 | }; 112 | 113 | $scope.createProcess = function(mode) { 114 | var modalInstance = _internalCreateModal({ 115 | template: 'views/popup/process-create.html?version=' + Date.now() 116 | }, $modal, $scope); 117 | }; 118 | 119 | $scope.importProcess = function () { 120 | _internalCreateModal({ 121 | template: 'views/popup/process-import.html?version=' + Date.now() 122 | }, $modal, $scope); 123 | }; 124 | 125 | $scope.showProcessDetails = function(process) { 126 | if (process) { 127 | $rootScope.editorHistory = []; 128 | $location.path("/processes/" + process.id); 129 | } 130 | }; 131 | 132 | $scope.editProcessDetails = function(process) { 133 | if (process) { 134 | $rootScope.editorHistory = []; 135 | $location.path("/editor/" + process.id); 136 | } 137 | }; 138 | 139 | // Finally, load initial processes 140 | $scope.loadProcesses(); 141 | }]); 142 | 143 | angular.module('flowableModeler') 144 | .controller('CreateNewProcessModelCtrl', ['$rootScope', '$scope', '$modal', '$http', '$location', 145 | function ($rootScope, $scope, $modal, $http, $location) { 146 | 147 | $scope.model = { 148 | loading: false, 149 | process: { 150 | name: '', 151 | key: '', 152 | description: '', 153 | modelType: 0 154 | } 155 | }; 156 | 157 | if ($scope.initialModelType !== undefined) { 158 | $scope.model.process.modelType = $scope.initialModelType; 159 | } 160 | 161 | $scope.ok = function () { 162 | 163 | if (!$scope.model.process.name || $scope.model.process.name.length == 0 || 164 | !$scope.model.process.key || $scope.model.process.key.length == 0) { 165 | 166 | return; 167 | } 168 | 169 | $scope.model.loading = true; 170 | 171 | $http({method: 'POST', url: FLOWABLE.APP_URL.getModelsUrl(), data: $scope.model.process}). 172 | success(function(data) { 173 | $scope.$hide(); 174 | 175 | $scope.model.loading = false; 176 | $rootScope.editorHistory = []; 177 | $location.path("/editor/" + data.id); 178 | }). 179 | error(function(data, status, headers, config) { 180 | $scope.model.loading = false; 181 | $scope.model.errorMessage = data.message; 182 | }); 183 | }; 184 | 185 | $scope.cancel = function () { 186 | if(!$scope.model.loading) { 187 | $scope.$hide(); 188 | } 189 | }; 190 | }]); 191 | 192 | angular.module('flowableModeler') 193 | .controller('DuplicateProcessModelCtrl', ['$rootScope', '$scope', '$modal', '$http', '$location', 194 | function ($rootScope, $scope, $modal, $http, $location) { 195 | 196 | $scope.model = { 197 | loading: false, 198 | process: { 199 | name: '', 200 | key: '', 201 | description: '' 202 | } 203 | }; 204 | 205 | if ($scope.originalModel) { 206 | //clone the model 207 | $scope.model.process.name = $scope.originalModel.process.name; 208 | $scope.model.process.key = $scope.originalModel.process.key; 209 | $scope.model.process.description = $scope.originalModel.process.description; 210 | $scope.model.process.id = $scope.originalModel.process.id; 211 | $scope.model.process.modelType = $scope.originalModel.process.modelType; 212 | } 213 | 214 | $scope.ok = function () { 215 | 216 | if (!$scope.model.process.name || $scope.model.process.name.length == 0 || 217 | !$scope.model.process.key || $scope.model.process.key.length == 0) { 218 | 219 | return; 220 | } 221 | 222 | $scope.model.loading = true; 223 | 224 | $http({method: 'POST', url: FLOWABLE.APP_URL.getCloneModelsUrl($scope.model.process.id), data: $scope.model.process}). 225 | success(function(data) { 226 | $scope.$hide(); 227 | 228 | $scope.model.loading = false; 229 | $rootScope.editorHistory = []; 230 | $location.path("/editor/" + data.id); 231 | }). 232 | error(function(data, status, headers, config) { 233 | $scope.model.loading = false; 234 | $scope.model.errorMessage = data.message; 235 | }); 236 | }; 237 | 238 | $scope.cancel = function () { 239 | if(!$scope.model.loading) { 240 | $scope.$hide(); 241 | } 242 | }; 243 | }]); 244 | 245 | angular.module('flowableModeler') 246 | .controller('ImportProcessModelCtrl', ['$rootScope', '$scope', '$http', 'Upload', '$location', function ($rootScope, $scope, $http, Upload, $location) { 247 | 248 | $scope.model = { 249 | loading: false 250 | }; 251 | 252 | $scope.onFileSelect = function($files, isIE) { 253 | 254 | $scope.model.loading = true; 255 | 256 | for (var i = 0; i < $files.length; i++) { 257 | var file = $files[i]; 258 | 259 | var url; 260 | if (isIE) { 261 | url = FLOWABLE.APP_URL.getImportProcessModelTextUrl(); 262 | } else { 263 | url = FLOWABLE.APP_URL.getImportProcessModelUrl(); 264 | } 265 | 266 | Upload.upload({ 267 | url: url, 268 | method: 'POST', 269 | file: file 270 | }).progress(function(evt) { 271 | $scope.model.uploadProgress = parseInt(100.0 * evt.loaded / evt.total); 272 | 273 | }).success(function(data) { 274 | $scope.model.loading = false; 275 | 276 | $location.path("/editor/" + data.id); 277 | $scope.$hide(); 278 | 279 | }).error(function(data) { 280 | 281 | if (data && data.message) { 282 | $scope.model.errorMessage = data.message; 283 | } 284 | 285 | $scope.model.error = true; 286 | $scope.model.loading = false; 287 | }); 288 | } 289 | }; 290 | 291 | $scope.cancel = function () { 292 | if(!$scope.model.loading) { 293 | $scope.$hide(); 294 | } 295 | }; 296 | }]); 297 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/resource-loader.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | (function(resources){ 14 | 15 | if (resources) { 16 | 17 | // Pause angular bootstrap so we have time to register and override angular services/directives etc 18 | window.name = 'NG_DEFER_BOOTSTRAP!'; 19 | 20 | function load(res, node, callback, scope) { 21 | var resource; 22 | if (res.tag === 'script') { 23 | resource = document.createElement('script'); 24 | resource.type = res.type || 'text/javascript'; 25 | resource.src = res.src; 26 | 27 | if (callback) { 28 | var done = false; 29 | 30 | // Attach handlers for all browsers 31 | resource.onload = resource.onreadystatechange = function() 32 | { 33 | if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) 34 | { 35 | done = true; 36 | callback.call(scope ? scope : this, res); 37 | } 38 | }; 39 | } 40 | } 41 | else if (res.tag === 'link') { 42 | resource = document.createElement('link'); 43 | resource.rel = res.rel || 'stylesheet'; 44 | resource.href = res.href; 45 | } 46 | 47 | if (node.nextSibling) { 48 | node.parentNode.insertBefore(resource, node.nextSibling); 49 | } 50 | else { 51 | node.parentNode.appendChild(resource); 52 | } 53 | 54 | if (res.tag === 'link' && callback) { 55 | callback.call(scope ? scope : this, res); 56 | } 57 | } 58 | 59 | function getResourceLoaderElement() { 60 | var scripts = document.getElementsByTagName('script'); 61 | for (var i = 0, il = scripts.length; i < il; i++) { 62 | if (scripts[i].src.indexOf('scripts/resource-loader.js') != -1) { 63 | return scripts[i]; 64 | } 65 | } 66 | return null; 67 | } 68 | 69 | var res = resources['*']; 70 | var resourceLoaderElement = getResourceLoaderElement(); 71 | var appName = resourceLoaderElement.getAttribute('app'); 72 | if (resources.hasOwnProperty(appName)) { 73 | res = resources[appName]; 74 | } 75 | 76 | var loadedResources = 0; 77 | for (var i = 0, il = res.length; i < il; i++) { 78 | load(res[i], resourceLoaderElement, function(){ 79 | loadedResources++; 80 | if (loadedResources == res.length) { 81 | // Let angular resume bootstrap 82 | var interval = window.setInterval(function(){ 83 | if (angular && typeof angular.resumeBootstrap == 'function') { 84 | angular.resumeBootstrap(); 85 | window.clearInterval(interval); 86 | } 87 | }, 20); 88 | 89 | } 90 | }); 91 | } 92 | } 93 | 94 | })(FLOWABLE.CONFIG.resources); -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/services/decision-table-service.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2005-2015 Alfresco Software, Ltd. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 'use strict'; 16 | 17 | // Decision Table service 18 | angular.module('flowableModeler').service('DecisionTableService', [ '$rootScope', '$http', '$q', '$timeout', '$translate', 19 | function ($rootScope, $http, $q, $timeout, $translate) { 20 | 21 | var httpAsPromise = function(options) { 22 | var deferred = $q.defer(); 23 | $http(options). 24 | success(function (response, status, headers, config) { 25 | deferred.resolve(response); 26 | }) 27 | .error(function (response, status, headers, config) { 28 | console.log('Something went wrong during http call:' + response); 29 | deferred.reject(response); 30 | }); 31 | return deferred.promise; 32 | }; 33 | 34 | this.filterDecisionTables = function(filter) { 35 | return httpAsPromise( 36 | { 37 | method: 'GET', 38 | url: FLOWABLE.APP_URL.getDecisionTableModelsUrl(), 39 | params: {filter: filter} 40 | } 41 | ); 42 | }; 43 | 44 | /** 45 | * Fetches the details of a decision table. 46 | */ 47 | this.fetchDecisionTableDetails = function(modelId, historyModelId) { 48 | var url = historyModelId ? 49 | FLOWABLE.APP_URL.getDecisionTableModelHistoryUrl(encodeURIComponent(modelId), encodeURIComponent(historyModelId)) : 50 | FLOWABLE.APP_URL.getDecisionTableModelUrl(encodeURIComponent(modelId)); 51 | return httpAsPromise({ method: 'GET', url: url }); 52 | }; 53 | 54 | function cleanUpModel (decisionTableDefinition) { 55 | delete decisionTableDefinition.isEmbeddedTable; 56 | var expressions = (decisionTableDefinition.inputExpressions || []).concat(decisionTableDefinition.outputExpressions || []); 57 | if (decisionTableDefinition.rules && decisionTableDefinition.rules.length > 0) { 58 | decisionTableDefinition.rules.forEach(function (rule) { 59 | var headerExpressionIds = []; 60 | expressions.forEach(function(def){ 61 | headerExpressionIds.push(def.id); 62 | }); 63 | 64 | // Make sure that the rule has all header ids defined as attribtues 65 | headerExpressionIds.forEach(function(id){ 66 | if (!rule.hasOwnProperty(id)) { 67 | rule[id] = ""; 68 | } 69 | }); 70 | 71 | // Make sure that the rule does not have an attribute that is not a header id 72 | delete rule.$$hashKey; 73 | for (var id in rule) { 74 | if (headerExpressionIds.indexOf(id) === -1) { 75 | delete rule[id]; 76 | delete rule.validationErrorMessages; 77 | } 78 | } 79 | 80 | }); 81 | } 82 | } 83 | 84 | this.saveDecisionTable = function (data, name, key, description, saveCallback, errorCallback) { 85 | 86 | data.decisionTableRepresentation = { 87 | name: name, 88 | key: key 89 | }; 90 | 91 | if (description && description.length > 0) { 92 | data.decisionTableRepresentation.description = description; 93 | } 94 | 95 | var decisionTableDefinition = angular.copy($rootScope.currentDecisionTable); 96 | 97 | data.decisionTableRepresentation.decisionTableDefinition = decisionTableDefinition; 98 | decisionTableDefinition.modelVersion = '2'; 99 | decisionTableDefinition.key = key; 100 | decisionTableDefinition.rules = angular.copy($rootScope.currentDecisionTableRules); 101 | 102 | html2canvas(jQuery('#decision-table-editor'), { 103 | onrendered: function (canvas) { 104 | var scale = canvas.width / 300.0; 105 | 106 | var extra_canvas = document.createElement('canvas'); 107 | extra_canvas.setAttribute('width', 300); 108 | extra_canvas.setAttribute('height', canvas.height / scale); 109 | 110 | var ctx = extra_canvas.getContext('2d'); 111 | ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, 300, canvas.height / scale); 112 | 113 | data.decisionTableImageBase64 = extra_canvas.toDataURL('image/png'); 114 | 115 | $http({ 116 | method: 'PUT', 117 | url: FLOWABLE.APP_URL.getDecisionTableModelUrl($rootScope.currentDecisionTable.id), 118 | data: data}). 119 | 120 | success(function (response, status, headers, config) { 121 | 122 | if (saveCallback) { 123 | saveCallback(); 124 | } 125 | }). 126 | error(function (response, status, headers, config) { 127 | if (errorCallback) { 128 | errorCallback(response); 129 | } 130 | }); 131 | } 132 | }); 133 | }; 134 | 135 | this.getDecisionTables = function (decisionTableIds, callback) { 136 | 137 | if (decisionTableIds.length > 0) { 138 | 139 | var decisionTableIdParams = ''; 140 | for (var i = 0; i < decisionTableIds.length; i++) { 141 | if (decisionTableIdParams.length > 0) { 142 | decisionTableIdParams += '&'; 143 | } 144 | decisionTableIdParams += 'decisionTableId=' + decisionTableIds[i]; 145 | } 146 | if (decisionTableIdParams.length > 0) { 147 | decisionTableIdParams += '&'; 148 | } 149 | decisionTableIdParams += 'version=' + Date.now(); 150 | 151 | $http({method: 'GET', url: FLOWABLE.APP_URL.getDecisionTableModelValuesUrl(decisionTableIdParams)}). 152 | success(function (data) { 153 | if (callback) { 154 | callback(data); 155 | } 156 | }). 157 | 158 | error(function (data) { 159 | console.log('Something went wrong when fetching decision table(s):' + JSON.stringify(data)); 160 | }); 161 | 162 | } else { 163 | if (callback) { 164 | callback(); 165 | } 166 | } 167 | }; 168 | 169 | }]); 170 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/services/identity-services.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 'use strict'; 14 | 15 | angular.module('flowableModeler').service('UserService', ['$http', '$q', 16 | function ($http, $q) { 17 | 18 | var httpAsPromise = function(options) { 19 | var deferred = $q.defer(); 20 | $http(options). 21 | success(function (response, status, headers, config) { 22 | deferred.resolve(response); 23 | }) 24 | .error(function (response, status, headers, config) { 25 | deferred.reject(response); 26 | }); 27 | return deferred.promise; 28 | }; 29 | 30 | /* 31 | * Filter users based on a filter text. 32 | */ 33 | this.getFilteredUsers = function (filterText, taskId, processInstanceId) { 34 | var params = {filter: filterText}; 35 | if(taskId) { 36 | params.excludeTaskId = taskId; 37 | } 38 | if (processInstanceId) { 39 | params.exclusdeProcessId = processInstanceId; 40 | } 41 | 42 | return httpAsPromise({ 43 | method: 'GET', 44 | url: FLOWABLE.APP_URL.getEditorUsersUrl(), 45 | params: params 46 | }); 47 | }; 48 | 49 | }]); 50 | 51 | angular.module('flowableModeler').service('GroupService', ['$http', '$q', 52 | function ($http, $q) { 53 | 54 | var httpAsPromise = function(options) { 55 | var deferred = $q.defer(); 56 | $http(options). 57 | success(function (response, status, headers, config) { 58 | deferred.resolve(response); 59 | }) 60 | .error(function (response, status, headers, config) { 61 | deferred.reject(response); 62 | }); 63 | return deferred.promise; 64 | }; 65 | 66 | /* 67 | * Filter functional groups based on a filter text. 68 | */ 69 | this.getFilteredGroups = function (filterText) { 70 | var params; 71 | if(filterText) { 72 | params = {filter: filterText}; 73 | } 74 | 75 | return httpAsPromise({ 76 | method: 'GET', 77 | url: FLOWABLE.APP_URL.getEditorGroupsUrl(), 78 | params: params 79 | }); 80 | }; 81 | }]); 82 | -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/scripts/services/util-services.js: -------------------------------------------------------------------------------- 1 | /* Licensed under the Apache License, Version 2.0 (the "License"); 2 | * you may not use this file except in compliance with the License. 3 | * You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | /** 14 | * Service with small utility methods 15 | */ 16 | angular.module('flowableModeler').service('UtilityService', [ '$window', '$document', '$timeout', function ($window, $document, $timeout) { 17 | 18 | this.scrollToElement = function(elementId) { 19 | $timeout(function() { 20 | var someElement = angular.element(document.getElementById(elementId))[0]; 21 | if (someElement) { 22 | if (someElement.getBoundingClientRect().top > $window.innerHeight) { 23 | $document.scrollToElement(someElement, 0, 1000); 24 | } 25 | } 26 | }); 27 | }; 28 | 29 | }]); -------------------------------------------------------------------------------- /src/main/resources/static/flowable-modeler/styles/common/style-retina.css: -------------------------------------------------------------------------------- 1 | /* Retina tweaks */ 2 | @media only screen and (-webkit-min-device-pixel-ratio: 1.5), 3 | only screen and ( min--moz-device-pixel-ratio: 1.5), 4 | only screen and ( -o-min-device-pixel-ratio: 3/2), 5 | only screen and ( min-device-pixel-ratio: 1.5), 6 | only screen and (min-resolution: 192dpi) { 7 | 8 | .navbar-header .navbar-brand { 9 | background-size: 148px 25px; 10 | } 11 | 12 | .account .google-drive { 13 | background: transparent url('../../images/google-drive-2x.png') 50% 50% no-repeat; 14 | background-size: 34px 34px; 15 | } 16 | 17 | .account .alfresco { 18 | background: transparent url('../../images/alfresco-2x.png') 50% 50% no-repeat; 19 | background-size: 34px 34px; 20 | } 21 | 22 | .account .alfresco-cloud { 23 | background: transparent url('../../images/alfresco-cloud-2x.png') 50% 50% no-repeat; 24 | background-size: 34px 34px; 25 | } 26 | } -------------------------------------------------------------------------------- /src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------