Skip to content

Commit

Permalink
Merge pull request #166 from ansibleguy76/release/v5.0.1
Browse files Browse the repository at this point in the history
v5.0.1 into main
  • Loading branch information
ansibleguy76 authored Apr 10, 2024
2 parents 49aff4d + 39ae923 commit 8b45c00
Show file tree
Hide file tree
Showing 27 changed files with 340 additions and 152 deletions.
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [5.0.1] - 2024-04-10

### Fixed

- login expiryDays didn't work
- fixed non-ascii codes in ldap
- fixed approvals, broken since 4.0.19

### Added

- add a form-reload route
- added mail attribute ldap
- improved error message on unevaluated fields
- allow to model arrays like foo.bar[0].ping.pong[1]
- allow placeholders in description fields of field validation

## [5.0.0] - 2024-01-25

### Added
Expand Down Expand Up @@ -650,7 +666,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow change password for current local user
- Start tracking versions

[Unreleased]: https://github.com/ansibleguy76/ansibleforms/compare/5.0.0...HEAD
[Unreleased]: https://github.com/ansibleguy76/ansibleforms/compare/5.0.1...HEAD

[5.0.1]: https://github.com/ansibleguy76/ansibleforms/compare/5.0.0...5.0.1

[5.0.0]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.19...5.0.0

Expand Down
4 changes: 2 additions & 2 deletions app_versions.gradle
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ext.version_code = 50000
ext.version_name = "5.0.0"
ext.version_code = 50001
ext.version_name = "5.0.1"
7 changes: 4 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ansible_forms_vue",
"version": "5.0.0",
"version": "5.0.1",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
Expand All @@ -12,9 +12,9 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "~3.34.0",
"core-js": "~3.36.1",
"vue": "~2.7.15",
"axios": "~1.6.2",
"axios": "~1.6.8",
"es6-promise": "~4.2.8",
"vuelidate": "~0.7.7",
"vue-router": "~3.5.4",
Expand All @@ -24,6 +24,7 @@
"bulmaswatch": "0.8.1",
"bulma" : "0.9.4",
"bulma-quickview" : "*",
"@creativebulma/bulma-tooltip" : "*",
"lodash" : "~4.17.21",
"bulma-checkradio": "~2.1.3",
"bulma-pageloader": "~0.3.0",
Expand Down
1 change: 1 addition & 0 deletions client/public/assets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@import "variables";
@import "bulma";
@import "overrides";
@import "@creativebulma/bulma-tooltip";

article.message table {
tr:last-child {
Expand Down
186 changes: 132 additions & 54 deletions client/src/components/Form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
<span class="icon is-pulled-right" v-show="loopbusy">
<font-awesome-icon icon="spinner" size="lg" class="has-text-warning" spin />
</span>
<span class="icon is-pulled-right has-tooltip-arrow has-tooltip-multiline has-tooltip-warning" v-show="!canSubmit && !loopbusy" :data-tooltip="unevaluatedFieldsWarning">
<font-awesome-icon icon="exclamation-triangle" size="lg" class="has-text-warning" />
</span>
<!-- reload button -->
<button @click="reloadForm" class="button is-white is-small mr-3">
<span class="has-text-info icon">
Expand Down Expand Up @@ -362,13 +365,13 @@
<p class="has-text-danger" v-if="'maxLength' in $v.form[field.name] && !$v.form[field.name].maxLength">Can not be more than {{$v.form[field.name].$params.maxLength.max}} characters long</p>
<p class="has-text-danger" v-if="'minValue' in $v.form[field.name] && !$v.form[field.name].minValue">Value cannot be lower than {{$v.form[field.name].$params.minValue.min}}</p>
<p class="has-text-danger" v-if="'maxValue' in $v.form[field.name] && !$v.form[field.name].maxValue">Value cannot be higher than {{$v.form[field.name].$params.maxValue.max}}</p>
<p class="has-text-danger" v-if="'minSize' in $v.form[field.name] && !$v.form[field.name].minSize">{{$v.form[field.name].$params.minSize.description}}</p>
<p class="has-text-danger" v-if="'maxSize' in $v.form[field.name] && !$v.form[field.name].maxSize">{{$v.form[field.name].$params.maxSize.description}}</p>
<p class="has-text-danger" v-if="'regex' in $v.form[field.name] && !$v.form[field.name].regex">{{$v.form[field.name].$params.regex.description}}</p>
<p class="has-text-danger" v-if="'validIf' in $v.form[field.name] && !$v.form[field.name].validIf">{{$v.form[field.name].$params.validIf.description}}</p>
<p class="has-text-danger" v-if="'validIfNot' in $v.form[field.name] && !$v.form[field.name].validIfNot">{{$v.form[field.name].$params.validIfNot.description}}</p>
<p class="has-text-danger" v-if="'notIn' in $v.form[field.name] && !$v.form[field.name].notIn">{{$v.form[field.name].$params.notIn.description}}</p>
<p class="has-text-danger" v-if="'in' in $v.form[field.name] && !$v.form[field.name].in">{{$v.form[field.name].$params.in.description}}</p>
<p class="has-text-danger" v-if="'minSize' in $v.form[field.name] && !$v.form[field.name].minSize">{{ replacePlaceholderInString($v.form[field.name].$params.minSize.description,true).value }}</p>
<p class="has-text-danger" v-if="'maxSize' in $v.form[field.name] && !$v.form[field.name].maxSize">{{ replacePlaceholderInString($v.form[field.name].$params.maxSize.description,true).value }}</p>
<p class="has-text-danger" v-if="'regex' in $v.form[field.name] && !$v.form[field.name].regex">{{ replacePlaceholderInString($v.form[field.name].$params.regex.description,true).value }}</p>
<p class="has-text-danger" v-if="'validIf' in $v.form[field.name] && !$v.form[field.name].validIf">{{ replacePlaceholderInString($v.form[field.name].$params.validIf.description,true).value }}</p>
<p class="has-text-danger" v-if="'validIfNot' in $v.form[field.name] && !$v.form[field.name].validIfNot">{{ replacePlaceholderInString($v.form[field.name].$params.validIfNot.description,true).value }}</p>
<p class="has-text-danger" v-if="'notIn' in $v.form[field.name] && !$v.form[field.name].notIn">{{ replacePlaceholderInString($v.form[field.name].$params.notIn.description,true).value }}</p>
<p class="has-text-danger" v-if="'in' in $v.form[field.name] && !$v.form[field.name].in">{{ replacePlaceholderInString($v.form[field.name].$params.in.description,true).value }}</p>
<p class="has-text-danger" v-if="'sameAs' in $v.form[field.name] && !$v.form[field.name].sameAs">Field must be identical to '{{$v.form[field.name].$params.sameAs.eq}}'</p>
</div>
</div>
Expand All @@ -380,7 +383,7 @@
<!-- job buttons -->
<hr v-if="!!currentForm" />
<!-- play button -->
<button v-if="!!currentForm && !jobResult.message" class="button is-primary is-fullwidth" @click="jobResult.message='initializing'"><span class="icon"><font-awesome-icon icon="play" /></span><span>Run</span></button>
<button v-if="!!currentForm && !jobResult.message" class="button is-primary is-fullwidth has-tooltip-multiline has-tooltip-arrow has-tooltip-warning" :data-tooltip="(!canSubmit)?unevaluatedFieldsWarning:undefined" :disabled="!canSubmit" @click="jobResult.message='initializing'"><span class="icon"><font-awesome-icon icon="play" /></span><span>Run</span></button>
<div class="columns">
<!-- progress/close button -->
<div class="column">
Expand Down Expand Up @@ -513,6 +516,7 @@
subjob:{}, // output of last subjob
dynamicFieldDependencies:{}, // which fields need to be re-evaluated if other fields change
dynamicFieldDependentOf:{}, // which fields are dependend from others
unevaluatedFields:[], // list of unevaluatedFields
defaults:{}, // holds default info per field
dynamicFieldStatus:{}, // holds the status of dynamics fields (running=currently being evaluated, variable=depends on others, fixed=only need 1-time lookup, default=has defaulted, undefined=trigger eval/query)
queryresults:{}, // holds the results of dynamic dropdown boxes
Expand Down Expand Up @@ -654,6 +658,14 @@
return obj
},
computed: {
// unevaluatedFieldsWarning
unevaluatedFieldsWarning(){
if(this.canSubmit){
return undefined
}else{
return this.unevaluatedFields.join(",") + " " + ((this.unevaluatedFields.length==1)?"is":"are") + " unevaluated..."
}
},
// joboutput
filteredJobOutput(){
if(!this.filterOutput) return this.jobResult?.data?.output?.replace(/\r\n/g,"<br>")
Expand Down Expand Up @@ -1396,6 +1408,7 @@
//console.log("enter loop");
// ref.$toast("... loop ...")
hasUnevaluatedFields=false; // reset flag
ref.unevaluatedFields=[]; // the list of unevaluatedfields - reset
// console.log("-------------------------------")
ref.currentForm.fields.forEach(
function(item,index){
Expand All @@ -1408,6 +1421,7 @@
// console.log("eval expression " + item.name)
// console.log(`[${item.name}][${flag}] : evaluating`)
hasUnevaluatedFields=true // set the un-eval flag if this is required
ref.unevaluatedFields.push(item.name)
// set flag running
ref.setFieldStatus(item.name,"running",false)
placeholderCheck = ref.replacePlaceholders(item) // check and replace placeholders
Expand Down Expand Up @@ -1524,6 +1538,7 @@
// console.log("eval query : " + item.name)
// set flag running
hasUnevaluatedFields=true
ref.unevaluatedFields.push(item.name)
ref.setFieldStatus(item.name,"running",false)
placeholderCheck = ref.replacePlaceholders(item) // check and replace placeholders
if(placeholderCheck.value!=undefined){ // expression is clean ?
Expand Down Expand Up @@ -1653,6 +1668,7 @@
if(ref.watchdog>50){ // is it taking too long ?
ref.jobResult.message="" // stop and reset
ref.$toast.warning("It took too long to evaluate all fields before run.\r\nLet the form stabilize and try again.")
ref.$toast.warning(ref.unevaluatedFieldsWarning)
}else{
//ref.$toast.info(`Stabilizing form...${ref.watchdog}`)
}
Expand Down Expand Up @@ -1839,58 +1855,120 @@
generateJsonOutput(filedata={}){
var ref=this
var formdata={}
this.currentForm.fields.forEach((item, i) => {
// this.checkDependencies(item) // hide field based on dependency
if(this.visibility[item.name] && !item.noOutput){
var fieldmodel = [].concat(item.model || [])
var outputObject = item.outputObject || item.type=="expression" || item.type=="file" || item.type=="table" || false
var outputValue = undefined
// if uploaded file info, use that
if(item.name in filedata){
outputValue=filedata[item.name]
// else just use the formdata
}else{
// deep clone, otherwise weird effects
outputValue = Helpers.deepClone(this.form[item.name])
}
// if no model is given, we assign to the root
if(!outputObject){ // do we need to flatten output ?
outputValue=this.getFieldValue(outputValue,item.valueColumn || "",true)
}
if(fieldmodel.length==0){
// deep clone = otherwise weird effects
formdata[item.name]=Helpers.deepClone(outputValue)
}else{
fieldmodel.forEach((f)=>{
// convert fieldmodel for actual object
// svm.lif.name => svm["lif"].name = formvalue
// using reduce, which is a recursive function
f.split(/\s*\.\s*/).reduce((master,obj, level,arr) => {
// if last
if (level === (arr.length - 1)){
try{
this.currentForm.fields.forEach((item, i) => {
// this.checkDependencies(item) // hide field based on dependency
if(this.visibility[item.name] && !item.noOutput){
var fieldmodel = [].concat(item.model || [])
var outputObject = item.outputObject || item.type=="expression" || item.type=="file" || item.type=="table" || false
var outputValue = undefined
// if uploaded file info, use that
if(item.name in filedata){
outputValue=filedata[item.name]
// else just use the formdata
}else{
// deep clone, otherwise weird effects
outputValue = Helpers.deepClone(this.form[item.name])
}
// if no model is given, we assign to the root
if(!outputObject){ // do we need to flatten output ?
outputValue=this.getFieldValue(outputValue,item.valueColumn || "",true)
}
if(fieldmodel.length==0){
// deep clone = otherwise weird effects
formdata[item.name]=Helpers.deepClone(outputValue)
}else{
// here we build the full model object
fieldmodel.forEach((f)=>{
// convert fieldmodel for actual object
// svm.lif.name => svm["lif"].name = formvalue
// using reduce, which is a recursive function
f.split(/\s*\.\s*/).reduce((master,obj, level,arr) => {
var arrsplit = undefined
// if last
if (level === (arr.length - 1)){
// the last piece we assign the value to
if(master[obj]===undefined){
master[obj]=outputValue
}else{
master[obj]=Lodash.merge(master[obj],outputValue)
// if the last piece is an array, we need to create the array first
if(obj.match(/.*\[[0-9]+\]$/)){
// split the array name and index
arrsplit=obj.split(/\[([0-9]+)\]$/)
// if the array doesn't exist, we create it
if(master[arrsplit[0]]===undefined){
master[arrsplit[0]]=[]
}
// if the array index doesn't exist, we create it
if(master[arrsplit[0]][arrsplit[1]]===undefined){
master[arrsplit[0]][arrsplit[1]]={}
}
// assign the value to the array index
master[arrsplit[0]][arrsplit[1]]=outputValue
return master[arrsplit[0]][arrsplit[1]]
}else{ // just an object
// if the object doesn't exist, we create it
if(master[obj]===undefined){
master[obj]=outputValue
}else{
master[obj]=Lodash.merge(master[obj],outputValue)
}
// return the result for next reduce iteration
return master[obj]
}
}else{
// initialize first time to object
if(master[obj]===undefined){
master[obj]={}
}else{
// initialize first time to object or array
// if the object is an array, we need to create the array first
if(obj.match(/.*\[[0-9]+\]$/)){
// split the array name and index
arrsplit=obj.split(/\[([0-9]+)\]$/)
// if the array doesn't exist, we create it
if(master[arrsplit[0]]===undefined){
master[arrsplit[0]]=[]
}
// if the array index doesn't exist, we create it
if(master[arrsplit[0]][arrsplit[1]]===undefined){
master[arrsplit[0]][arrsplit[1]]={}
}
// return the result for next reduce iteration
return master[arrsplit[0]][arrsplit[1]]
}else{ // just an object
// if the object doesn't exist, we create it
if(master[obj]===undefined){
master[obj]={}
}
// return the result for next reduce iteration
return master[obj]
}
}
// return the result for next reduce iteration
return master[obj]
},formdata);
})
}
},formdata);
})
}
}
}
});
});
}catch(err){
console.log(err)
ref.$toast.error("Failed to generate json output.\r\nContact the developer.\r\n" + (err.message || err.toString()))
}
// update main data
Vue.set(this,"formdata",formdata)
},
Expand Down
5 changes: 5 additions & 0 deletions client/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Vue from 'vue'
import Router from 'vue-router'
import Forms from './views/Forms.vue'
import Form from './views/Form.vue'
import FormReload from './views/FormReload.vue'
import Login from './views/Login.vue'
import ErrorVue from './views/Error.vue'
import Schema from './views/Schema.vue'
Expand Down Expand Up @@ -60,6 +61,10 @@ export default new Router({
name:"Form",
component:Form
},
{
path: '/form-reload',
component:FormReload
},
{
path:"/login",
name:"Login",
Expand Down
20 changes: 20 additions & 0 deletions client/src/views/FormReload.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<div></div>
</template>
<script>
import Vue from 'vue'
export default{
name:"FormReloader",
data(){
return {
}
},
methods:{
},
mounted() { // when the Vue app is booted up, this is run automatically.
this.$router.push({name:'Form',query:this.$router.currentRoute.query})
}
}
</script>
<style scoped>
</style>
Loading

0 comments on commit 8b45c00

Please sign in to comment.