Repository: syncope
Updated Branches:
  refs/heads/master cb1ac36d8 -> 421c0f8b4


[SYNCOPE-719] aligned derived attibutes to admin console's


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/4d2cd5f9
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/4d2cd5f9
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/4d2cd5f9

Branch: refs/heads/master
Commit: 4d2cd5f9d6324ad81f44d93fd151d1c1853701f7
Parents: cb1ac36
Author: Andrea Patricelli <andrea.patrice...@tirasa.net>
Authored: Wed Dec 30 11:26:56 2015 +0100
Committer: Andrea Patricelli <andrea.patrice...@tirasa.net>
Committed: Wed Dec 30 11:26:56 2015 +0100

----------------------------------------------------------------------
 .../resources/META-INF/resources/app/index.html |   8 +-
 .../resources/META-INF/resources/app/js/app.js  |  28 ++-
 .../app/js/controllers/UserController.js        |  46 +++--
 .../app/js/directives/dynamicAttribute.js       | 198 -------------------
 .../js/directives/dynamicDerivedAttribute.js    |  43 ++++
 .../js/directives/dynamicDerivedAttributes.js   |   2 +-
 .../app/js/directives/dynamicPlainAttribute.js  | 198 +++++++++++++++++++
 .../js/directives/dynamicVirtualAttribute.js    | 198 +++++++++++++++++++
 .../resources/app/views/dynamicAttribute.html   |  58 ------
 .../app/views/dynamicDerivedAttribute.html      |   5 +
 .../app/views/dynamicDerivedAttributes.html     |  25 +--
 .../app/views/dynamicPlainAttribute.html        |  58 ++++++
 .../app/views/dynamicPlainAttributes.html       |   4 +-
 .../app/views/dynamicVirtualAttribute.html      |  58 ++++++
 .../META-INF/resources/app/views/editUser.html  |   4 +-
 15 files changed, 622 insertions(+), 311 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/index.html
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/index.html 
b/client/enduser/src/main/resources/META-INF/resources/app/index.html
index 117796b..8dd9961 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/index.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/index.html
@@ -69,7 +69,7 @@ under the License.
     <script 
src="../webjars/angular-cookies/${angular-cookies.version}/angular-cookies.js"></script>
     <script 
src="../webjars/angular-sanitize/${angular-sanitize.version}/angular-sanitize.js"></script>
     <script 
src="../webjars/angular-ui-bootstrap/${angular-ui-bootstrap.version}/ui-bootstrap-tpls.js"></script>
-    <script 
src="../webjars/angular-ui-select/${angular-ui-select.version}/select.js"></script>
+    <!--<script 
src="../webjars/angular-ui-select/${angular-ui-select.version}/select.js"></script>-->
     <script 
src="../webjars/angular-growl-2/${angular-growl-2.version}/angular-growl.js"></script>
     <script type="text/javascript" 
src="../webjars/bootstrap-select/${bootstrap-select.version}/js/bootstrap-select.min.js"></script>
     <script src="../webjars/FileSaver.js/${FileSaver.version}/FileSaver.js" 
type="text/javascript"></script>
@@ -89,7 +89,9 @@ under the License.
     <script src="js/controllers/LanguageController.js"></script>
     <script src="js/controllers/UserController.js"></script>
     <!--directives-->
-    <script src="js/directives/dynamicAttribute.js"></script>
+    <script src="js/directives/dynamicPlainAttribute.js"></script>
+    <script src="js/directives/dynamicDerivedAttribute.js"></script>
+    <script src="js/directives/dynamicVirtualAttribute.js"></script>
     <script src="js/directives/dynamicPlainAttributes.js"></script>
     <script src="js/directives/dynamicDerivedAttributes.js"></script>
     <script src="js/directives/dynamicVirtualAttributes.js"></script>
@@ -110,7 +112,7 @@ under the License.
     <link href="../webjars/ionicons/${ionicons.version}/css/ionicons.min.css" 
rel="stylesheet" type="text/css" />
     <link 
href="../webjars/angular-ui-select/${angular-ui-select.version}/select.css" 
rel="stylesheet" type="text/css"/>
     <link 
href="../webjars/angular-growl-2/${angular-growl-2.version}/angular-growl.css" 
rel="stylesheet" type="text/css"/>
-    <link href="../webjars/select2/${select2.version}/select2.css" 
rel="stylesheet" />
+    <!--<link href="../webjars/select2/${select2.version}/select2.css" 
rel="stylesheet" />-->
     <link href="css/app.css" rel="stylesheet" type="text/css" />
     <link href="css/login.css" rel="stylesheet" type="text/css" />
     <link href="css/editUser.css" rel="stylesheet" type="text/css" />

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js 
b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
index 43ea447..4253e11 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
@@ -29,7 +29,7 @@ angular.module('info', []);
 var app = angular.module('SyncopeEnduserApp', [
   'ui.router',
   'ui.bootstrap',
-  'ui.select',
+//  'ui.select',
   'ngSanitize',
   'ngAnimate',
   'ngResource',
@@ -83,15 +83,14 @@ app.config(['$stateProvider', '$urlRouterProvider', 
'$httpProvider', 'growlProvi
               url: '/plainSchemas',
               templateUrl: 'views/user-plain-schemas.html'
             })
-            /*.state('create.derivedSchemas', {
-              url: '/derivedSchemas',
-              templateUrl: 'views/user-derived-schemas.html'
-            })
-            .state('create.virtualSchemas', {
-              url: '/virtualSchemas',
-              templateUrl: 'views/user-virtual-schemas.html'
-            })*/
-            // url will be /self/create/schema
+            .state('create.derivedSchemas', {
+             url: '/derivedSchemas',
+             templateUrl: 'views/user-derived-schemas.html'
+             })
+             .state('create.virtualSchemas', {
+             url: '/virtualSchemas',
+             templateUrl: 'views/user-virtual-schemas.html'
+             })
             .state('create.resources', {
               url: '/resources',
               templateUrl: 'views/user-resources.html'
@@ -111,7 +110,7 @@ app.config(['$stateProvider', '$urlRouterProvider', 
'$httpProvider', 'growlProvi
             })
             // nested states 
             // each of these sections will have their own view
-            // url will be nested (/self/create)
+            // url will be nested (/self/update)
             .state('update.credentials', {
               url: '/credentials',
               templateUrl: 'views/user-credentials.html',
@@ -130,7 +129,7 @@ app.config(['$stateProvider', '$urlRouterProvider', 
'$httpProvider', 'growlProvi
                 }
               }
             })
-            /*.state('update.derivedSchemas', {
+            .state('update.derivedSchemas', {
               url: '/derivedSchemas',
               templateUrl: 'views/user-derived-schemas.html',
               resolve: {
@@ -147,8 +146,7 @@ app.config(['$stateProvider', '$urlRouterProvider', 
'$httpProvider', 'growlProvi
                   return AuthenticationHelper.authenticated();
                 }
               }
-            })*/
-            // url will be /self/create/schema
+            })
             .state('update.groups', {
               url: '/groups',
               templateUrl: 'views/user-groups.html',
@@ -228,7 +226,7 @@ app.config(['$stateProvider', '$urlRouterProvider', 
'$httpProvider', 'growlProvi
         }
       };
     });
-    
+
     growlProvider.globalTimeToLive(10000);
     growlProvider.globalPosition('bottom-left');
     growlProvider.globalInlineMessages(true);

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
 
b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
index 86ff297..6bf7cb6 100644
--- 
a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
@@ -45,8 +45,8 @@ angular.module("self").controller("UserController", 
['$scope', '$rootScope', '$l
         plainSchemas: [],
         derSchemas: [],
         virSchemas: [],
-        selectedDerSchemas: [],
-        selectedVirSchemas: [],
+//        selectedDerSchemas: [],
+//        selectedVirSchemas: [],
         errorMessage: '',
         attributeTable: {}
       };
@@ -96,8 +96,14 @@ angular.module("self").controller("UserController", 
['$scope', '$rootScope', '$l
 
             var derSchemaKey = schemas.derSchemas[i].key;
 
-            if ($scope.user.derAttrs[derSchemaKey]) {
-              
$scope.dynamicForm.selectedDerSchemas.push(schemas.derSchemas[i]);
+            if (!$scope.user.derAttrs[derSchemaKey]) {
+
+              $scope.user.derAttrs[derSchemaKey] = {
+                schema: derSchemaKey,
+                values: [],
+                readonly: true
+              };
+
             }
           }
 
@@ -106,8 +112,31 @@ angular.module("self").controller("UserController", 
['$scope', '$rootScope', '$l
 
             var virSchemaKey = schemas.virSchemas[i].key;
 
-            if ($scope.user.virAttrs[virSchemaKey]) {
-              
$scope.dynamicForm.selectedVirSchemas.push(schemas.virSchemas[i]);
+            if (!$scope.user.virAttrs[virSchemaKey]) {
+
+              $scope.user.virAttrs[virSchemaKey] = {
+                schema: virSchemaKey,
+                values: [],
+                readonly: schemas.virSchemas[i].readonly
+              };
+
+              // initialize multivalue schema and support table: create mode, 
only first value
+//              if (schemas.plainSchemas[i].multivalue) {
+//                
$scope.dynamicForm.attributeTable[schemas.plainSchemas[i].key] = {
+//                  fields: [schemas.plainSchemas[i].key + "_" + 0]
+//                };
+//              }
+            } else {
+              // initialize multivalue schema and support table: update mode, 
all provided values
+//              if (schemas.virSchemas[i].multivalue) {
+//                $scope.dynamicForm.attributeTable[schemas.virSchemas[i].key] 
= {
+//                  fields: [schemas.virSchemas[i].key + "_" + 0]
+//                };
+//                // add other values
+//                for (var j = 1; j < 
$scope.user.plainAttrs[plainSchemaKey].values.length; j++) {
+//                  
$scope.dynamicForm.attributeTable[schemas.plainSchemas[i].key].fields.push(schemas.plainSchemas[i].key
 + "_" + j);
+//                }
+//              }
             }
           }
 
@@ -174,15 +203,10 @@ angular.module("self").controller("UserController", 
['$scope', '$rootScope', '$l
         initUserRealm();
 
       } else {
-
         // read user from syncope core
         readUser();
-        // read user security question
-
       }
 
-
-
       initRealms();
       //retrieve security available questions
       initSecurityQuestions();

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicAttribute.js
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicAttribute.js
 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicAttribute.js
deleted file mode 100644
index 781d251..0000000
--- 
a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicAttribute.js
+++ /dev/null
@@ -1,198 +0,0 @@
-/* 
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-'use strict';
-
-angular.module('self')
-        .directive('dynamicAttribute', function ($filter) {
-          return {
-            restrict: 'E',
-            templateUrl: 'views/dynamicAttribute.html',
-            scope: {
-              schema: "=",
-              index: "=",
-              user: "="
-            },
-            controller: function ($scope, $element, $window) {
-              $scope.initAttribute = function (schema, index) {
-
-                switch (schema.type) {
-                  case "Long":
-                  case "Double":
-                    $scope.user.plainAttrs[schema.key].values[index] = 
Number($scope.user.plainAttrs[schema.key].values[index])
-                            || undefined;
-                    break;
-                  case "Enum":
-                    $scope.enumerationValues = [];
-                    var enumerationValuesSplitted = 
schema.enumerationValues.toString().split(";");
-                    for (var i = 0; i < enumerationValuesSplitted.length; i++) 
{
-                      
$scope.enumerationValues.push(enumerationValuesSplitted[i]);
-                    }
-                    $scope.user.plainAttrs[schema.key].values[index] = 
$scope.user.plainAttrs[schema.key].values[index]
-                            || $scope.enumerationValues[0];
-                    break;
-                  case "Binary":
-
-                    $scope.userFile = $scope.userFile || '';
-                    //for multivalue fields 
-//                    $scope.fileInputId = "fileInputId_" + index;
-
-                    $element.bind("change", function (changeEvent) {
-                      $scope.$apply(function () {
-                        var reader = new FileReader();
-                        var file = changeEvent.target.files[0];
-                        $scope.userFile = file.name;
-                        reader.onload = function (readerEvt) {
-                          var binaryString = readerEvt.target.result;
-                          $scope.user.plainAttrs[schema.key].values[index] = 
btoa(binaryString);
-                        };
-                        reader.readAsBinaryString(file);
-                      });
-                    });
-
-                    $scope.download = function () {
-                      var byteString = 
atob($scope.user.plainAttrs[schema.key].values[index]);
-
-                      var ab = new ArrayBuffer(byteString.length);
-                      var ia = new Uint8Array(ab);
-                      for (var i = 0; i < byteString.length; i++) {
-                        ia[i] = byteString.charCodeAt(i);
-                      }
-
-                      var blob = new Blob([ia], {type: schema.mimeType});
-
-                      saveAs(blob, schema.key);
-                    };
-                    $scope.remove = function () {
-                      $scope.user.plainAttrs[schema.key].values.splice(index, 
1);
-                      $scope.userFile = '';
-                      $("#fileInput").replaceWith($("#fileInput").clone(true));
-                    };
-                    break;
-                  case "Date":
-
-                    $scope.selectedDate = 
$scope.user.plainAttrs[schema.key].values[index];
-                    $scope.format = $scope.schema.conversionPattern;
-                    $scope.includeTimezone = false;
-                    if ($scope.schema.conversionPattern.indexOf(".SSS") > -1) {
-                      $scope.format = $scope.format.replace(".SSS", ".sss");
-                    }
-                    if ($scope.schema.conversionPattern.indexOf("Z") > -1) {
-                      $scope.includeTimezone = true;
-                      $scope.format = $scope.format.replace("Z", "");
-                    }
-                    if ($scope.schema.conversionPattern.indexOf("\'") > -1) {
-                      $scope.format = $scope.format.replace(new RegExp("\'", 
"g"), "");
-                    }
-
-                    $scope.bindDateToModel = function (selectedDate, format) {
-                      var newFormat = $scope.includeTimezone ? format.concat(" 
Z") : format;
-                      if (selectedDate) {
-                        selectedDate = $filter('date')(selectedDate, 
newFormat);
-                        var dateGood = selectedDate.toString();
-                        $scope.user.plainAttrs[schema.key].values[index] = 
dateGood;
-                      } else {
-                        $scope.user.plainAttrs[schema.key].values[index] = 
selectedDate;
-                      }
-                    };
-
-                    $scope.clear = function () {
-                      $scope.user.plainAttrs[schema.key].values[index] = null;
-                    };
-
-                    // Disable weekend selection
-                    $scope.disabled = function (date, mode) {
-                      // example if you want to disable weekends
-                      // return (mode === 'day' && (date.getDay() === 0 || 
date.getDay() === 6));
-                      return false;
-                    };
-
-                    $scope.toggleMin = function () {
-                      $scope.minDate = $scope.minDate ? null : new Date();
-                    };
-
-                    $scope.maxDate = new Date(2050, 5, 22);
-
-                    $scope.open = function ($event) {
-                      $scope.status.opened = true;
-                    };
-
-                    $scope.setDate = function (year, month, day) {
-                      $scope.user.plainAttrs[schema.key].values[index] = new 
Date(year, month, day);
-                    };
-
-                    $scope.dateOptions = {
-                      startingDay: 1
-                    };
-
-                    $scope.status = {
-                      opened: false
-                    };
-
-                    var tomorrow = new Date();
-                    tomorrow.setDate(tomorrow.getDate() + 1);
-                    var afterTomorrow = new Date();
-                    afterTomorrow.setDate(tomorrow.getDate() + 2);
-                    $scope.events =
-                            [
-                              {
-                                date: tomorrow,
-                                status: 'full'
-                              },
-                              {
-                                date: afterTomorrow,
-                                status: 'partially'
-                              }
-                            ];
-
-                    $scope.getDayClass = function (date, mode) {
-                      if (mode === 'day') {
-                        var dayToCheck = new Date(date).setHours(0, 0, 0, 0);
-
-                        for (var i = 0; i < $scope.events.length; i++) {
-                          var currentDay = new 
Date($scope.events[i].date).setHours(0, 0, 0, 0);
-
-                          if (dayToCheck === currentDay) {
-                            return $scope.events[i].status;
-                          }
-                        }
-                      }
-
-                    };
-                    break;
-
-                  case "Boolean":
-                    $scope.user.plainAttrs[schema.key].values[index] =
-                            
Boolean($scope.user.plainAttrs[schema.key].values[index]) || false;
-                    break;
-
-                }
-              };
-
-              $scope.$watch(function () {
-                return 
$scope.user.plainAttrs[$scope.schema.key].values[$scope.index];
-              }, function (newValue, oldValue) {
-                $scope.user.plainAttrs[$scope.schema.key].values = 
$scope.user.plainAttrs[$scope.schema.key].values
-                        .filter(function (n) {
-                          return (n !== undefined && n !== "");
-                        });
-              });
-            },
-            replace: true
-          };
-        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttribute.js
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttribute.js
 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttribute.js
new file mode 100644
index 0000000..ff3f4e3
--- /dev/null
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttribute.js
@@ -0,0 +1,43 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+'use strict';
+
+angular.module('self')
+        .directive('dynamicDerivedAttribute', function ($filter) {
+          return {
+            restrict: 'E',
+            templateUrl: 'views/dynamicDerivedAttribute.html',
+            scope: {
+              schema: "=",
+              index: "=",
+              user: "="
+            },
+            controller: function ($scope, $element, $window) {
+              $scope.$watch(function () {
+                return 
$scope.user.derAttrs[$scope.schema.key].values[$scope.index];
+              }, function (newValue, oldValue) {
+                $scope.user.derAttrs[$scope.schema.key].values = 
$scope.user.derAttrs[$scope.schema.key].values
+                        .filter(function (n) {
+                          return (n !== undefined && n !== "");
+                        });
+              });
+            },
+            replace: true
+          };
+        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttributes.js
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttributes.js
 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttributes.js
index 887b5c6..d2a0187 100644
--- 
a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttributes.js
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicDerivedAttributes.js
@@ -44,7 +44,7 @@ angular.module('self')
                 var derSchemaKey = item.key;
                 console.log("REMOVING DERIVED item: ", derSchemaKey);
                 delete $scope.user.derAttrs[derSchemaKey];
-                
+
               };
 
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttribute.js
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttribute.js
 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttribute.js
new file mode 100644
index 0000000..18dff97
--- /dev/null
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttribute.js
@@ -0,0 +1,198 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+'use strict';
+
+angular.module('self')
+        .directive('dynamicPlainAttribute', function ($filter) {
+          return {
+            restrict: 'E',
+            templateUrl: 'views/dynamicPlainAttribute.html',
+            scope: {
+              schema: "=",
+              index: "=",
+              user: "="
+            },
+            controller: function ($scope, $element, $window) {
+              $scope.initAttribute = function (schema, index) {
+
+                switch (schema.type) {
+                  case "Long":
+                  case "Double":
+                    $scope.user.plainAttrs[schema.key].values[index] = 
Number($scope.user.plainAttrs[schema.key].values[index])
+                            || undefined;
+                    break;
+                  case "Enum":
+                    $scope.enumerationValues = [];
+                    var enumerationValuesSplitted = 
schema.enumerationValues.toString().split(";");
+                    for (var i = 0; i < enumerationValuesSplitted.length; i++) 
{
+                      
$scope.enumerationValues.push(enumerationValuesSplitted[i]);
+                    }
+                    $scope.user.plainAttrs[schema.key].values[index] = 
$scope.user.plainAttrs[schema.key].values[index]
+                            || $scope.enumerationValues[0];
+                    break;
+                  case "Binary":
+
+                    $scope.userFile = $scope.userFile || '';
+                    //for multivalue fields 
+//                    $scope.fileInputId = "fileInputId_" + index;
+
+                    $element.bind("change", function (changeEvent) {
+                      $scope.$apply(function () {
+                        var reader = new FileReader();
+                        var file = changeEvent.target.files[0];
+                        $scope.userFile = file.name;
+                        reader.onload = function (readerEvt) {
+                          var binaryString = readerEvt.target.result;
+                          $scope.user.plainAttrs[schema.key].values[index] = 
btoa(binaryString);
+                        };
+                        reader.readAsBinaryString(file);
+                      });
+                    });
+
+                    $scope.download = function () {
+                      var byteString = 
atob($scope.user.plainAttrs[schema.key].values[index]);
+
+                      var ab = new ArrayBuffer(byteString.length);
+                      var ia = new Uint8Array(ab);
+                      for (var i = 0; i < byteString.length; i++) {
+                        ia[i] = byteString.charCodeAt(i);
+                      }
+
+                      var blob = new Blob([ia], {type: schema.mimeType});
+
+                      saveAs(blob, schema.key);
+                    };
+                    $scope.remove = function () {
+                      $scope.user.plainAttrs[schema.key].values.splice(index, 
1);
+                      $scope.userFile = '';
+                      $("#fileInput").replaceWith($("#fileInput").clone(true));
+                    };
+                    break;
+                  case "Date":
+
+                    $scope.selectedDate = 
$scope.user.plainAttrs[schema.key].values[index];
+                    $scope.format = $scope.schema.conversionPattern;
+                    $scope.includeTimezone = false;
+                    if ($scope.schema.conversionPattern.indexOf(".SSS") > -1) {
+                      $scope.format = $scope.format.replace(".SSS", ".sss");
+                    }
+                    if ($scope.schema.conversionPattern.indexOf("Z") > -1) {
+                      $scope.includeTimezone = true;
+                      $scope.format = $scope.format.replace("Z", "");
+                    }
+                    if ($scope.schema.conversionPattern.indexOf("\'") > -1) {
+                      $scope.format = $scope.format.replace(new RegExp("\'", 
"g"), "");
+                    }
+
+                    $scope.bindDateToModel = function (selectedDate, format) {
+                      var newFormat = $scope.includeTimezone ? format.concat(" 
Z") : format;
+                      if (selectedDate) {
+                        selectedDate = $filter('date')(selectedDate, 
newFormat);
+                        var dateGood = selectedDate.toString();
+                        $scope.user.plainAttrs[schema.key].values[index] = 
dateGood;
+                      } else {
+                        $scope.user.plainAttrs[schema.key].values[index] = 
selectedDate;
+                      }
+                    };
+
+                    $scope.clear = function () {
+                      $scope.user.plainAttrs[schema.key].values[index] = null;
+                    };
+
+                    // Disable weekend selection
+                    $scope.disabled = function (date, mode) {
+                      // example if you want to disable weekends
+                      // return (mode === 'day' && (date.getDay() === 0 || 
date.getDay() === 6));
+                      return false;
+                    };
+
+                    $scope.toggleMin = function () {
+                      $scope.minDate = $scope.minDate ? null : new Date();
+                    };
+
+                    $scope.maxDate = new Date(2050, 5, 22);
+
+                    $scope.open = function ($event) {
+                      $scope.status.opened = true;
+                    };
+
+                    $scope.setDate = function (year, month, day) {
+                      $scope.user.plainAttrs[schema.key].values[index] = new 
Date(year, month, day);
+                    };
+
+                    $scope.dateOptions = {
+                      startingDay: 1
+                    };
+
+                    $scope.status = {
+                      opened: false
+                    };
+
+                    var tomorrow = new Date();
+                    tomorrow.setDate(tomorrow.getDate() + 1);
+                    var afterTomorrow = new Date();
+                    afterTomorrow.setDate(tomorrow.getDate() + 2);
+                    $scope.events =
+                            [
+                              {
+                                date: tomorrow,
+                                status: 'full'
+                              },
+                              {
+                                date: afterTomorrow,
+                                status: 'partially'
+                              }
+                            ];
+
+                    $scope.getDayClass = function (date, mode) {
+                      if (mode === 'day') {
+                        var dayToCheck = new Date(date).setHours(0, 0, 0, 0);
+
+                        for (var i = 0; i < $scope.events.length; i++) {
+                          var currentDay = new 
Date($scope.events[i].date).setHours(0, 0, 0, 0);
+
+                          if (dayToCheck === currentDay) {
+                            return $scope.events[i].status;
+                          }
+                        }
+                      }
+
+                    };
+                    break;
+
+                  case "Boolean":
+                    $scope.user.plainAttrs[schema.key].values[index] =
+                            
Boolean($scope.user.plainAttrs[schema.key].values[index]) || false;
+                    break;
+
+                }
+              };
+
+              $scope.$watch(function () {
+                return 
$scope.user.plainAttrs[$scope.schema.key].values[$scope.index];
+              }, function (newValue, oldValue) {
+                $scope.user.plainAttrs[$scope.schema.key].values = 
$scope.user.plainAttrs[$scope.schema.key].values
+                        .filter(function (n) {
+                          return (n !== undefined && n !== "");
+                        });
+              });
+            },
+            replace: true
+          };
+        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicVirtualAttribute.js
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicVirtualAttribute.js
 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicVirtualAttribute.js
new file mode 100644
index 0000000..5ee0d11
--- /dev/null
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicVirtualAttribute.js
@@ -0,0 +1,198 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+'use strict';
+
+angular.module('self')
+        .directive('dynamicAttribute', function ($filter) {
+          return {
+            restrict: 'E',
+            templateUrl: 'views/dynamicPlainAttribute.html',
+            scope: {
+              schema: "=",
+              index: "=",
+              user: "="
+            },
+            controller: function ($scope, $element, $window) {
+              $scope.initAttribute = function (schema, index) {
+
+                switch (schema.type) {
+                  case "Long":
+                  case "Double":
+                    $scope.user.plainAttrs[schema.key].values[index] = 
Number($scope.user.plainAttrs[schema.key].values[index])
+                            || undefined;
+                    break;
+                  case "Enum":
+                    $scope.enumerationValues = [];
+                    var enumerationValuesSplitted = 
schema.enumerationValues.toString().split(";");
+                    for (var i = 0; i < enumerationValuesSplitted.length; i++) 
{
+                      
$scope.enumerationValues.push(enumerationValuesSplitted[i]);
+                    }
+                    $scope.user.plainAttrs[schema.key].values[index] = 
$scope.user.plainAttrs[schema.key].values[index]
+                            || $scope.enumerationValues[0];
+                    break;
+                  case "Binary":
+
+                    $scope.userFile = $scope.userFile || '';
+                    //for multivalue fields 
+//                    $scope.fileInputId = "fileInputId_" + index;
+
+                    $element.bind("change", function (changeEvent) {
+                      $scope.$apply(function () {
+                        var reader = new FileReader();
+                        var file = changeEvent.target.files[0];
+                        $scope.userFile = file.name;
+                        reader.onload = function (readerEvt) {
+                          var binaryString = readerEvt.target.result;
+                          $scope.user.plainAttrs[schema.key].values[index] = 
btoa(binaryString);
+                        };
+                        reader.readAsBinaryString(file);
+                      });
+                    });
+
+                    $scope.download = function () {
+                      var byteString = 
atob($scope.user.plainAttrs[schema.key].values[index]);
+
+                      var ab = new ArrayBuffer(byteString.length);
+                      var ia = new Uint8Array(ab);
+                      for (var i = 0; i < byteString.length; i++) {
+                        ia[i] = byteString.charCodeAt(i);
+                      }
+
+                      var blob = new Blob([ia], {type: schema.mimeType});
+
+                      saveAs(blob, schema.key);
+                    };
+                    $scope.remove = function () {
+                      $scope.user.plainAttrs[schema.key].values.splice(index, 
1);
+                      $scope.userFile = '';
+                      $("#fileInput").replaceWith($("#fileInput").clone(true));
+                    };
+                    break;
+                  case "Date":
+
+                    $scope.selectedDate = 
$scope.user.plainAttrs[schema.key].values[index];
+                    $scope.format = $scope.schema.conversionPattern;
+                    $scope.includeTimezone = false;
+                    if ($scope.schema.conversionPattern.indexOf(".SSS") > -1) {
+                      $scope.format = $scope.format.replace(".SSS", ".sss");
+                    }
+                    if ($scope.schema.conversionPattern.indexOf("Z") > -1) {
+                      $scope.includeTimezone = true;
+                      $scope.format = $scope.format.replace("Z", "");
+                    }
+                    if ($scope.schema.conversionPattern.indexOf("\'") > -1) {
+                      $scope.format = $scope.format.replace(new RegExp("\'", 
"g"), "");
+                    }
+
+                    $scope.bindDateToModel = function (selectedDate, format) {
+                      var newFormat = $scope.includeTimezone ? format.concat(" 
Z") : format;
+                      if (selectedDate) {
+                        selectedDate = $filter('date')(selectedDate, 
newFormat);
+                        var dateGood = selectedDate.toString();
+                        $scope.user.plainAttrs[schema.key].values[index] = 
dateGood;
+                      } else {
+                        $scope.user.plainAttrs[schema.key].values[index] = 
selectedDate;
+                      }
+                    };
+
+                    $scope.clear = function () {
+                      $scope.user.plainAttrs[schema.key].values[index] = null;
+                    };
+
+                    // Disable weekend selection
+                    $scope.disabled = function (date, mode) {
+                      // example if you want to disable weekends
+                      // return (mode === 'day' && (date.getDay() === 0 || 
date.getDay() === 6));
+                      return false;
+                    };
+
+                    $scope.toggleMin = function () {
+                      $scope.minDate = $scope.minDate ? null : new Date();
+                    };
+
+                    $scope.maxDate = new Date(2050, 5, 22);
+
+                    $scope.open = function ($event) {
+                      $scope.status.opened = true;
+                    };
+
+                    $scope.setDate = function (year, month, day) {
+                      $scope.user.plainAttrs[schema.key].values[index] = new 
Date(year, month, day);
+                    };
+
+                    $scope.dateOptions = {
+                      startingDay: 1
+                    };
+
+                    $scope.status = {
+                      opened: false
+                    };
+
+                    var tomorrow = new Date();
+                    tomorrow.setDate(tomorrow.getDate() + 1);
+                    var afterTomorrow = new Date();
+                    afterTomorrow.setDate(tomorrow.getDate() + 2);
+                    $scope.events =
+                            [
+                              {
+                                date: tomorrow,
+                                status: 'full'
+                              },
+                              {
+                                date: afterTomorrow,
+                                status: 'partially'
+                              }
+                            ];
+
+                    $scope.getDayClass = function (date, mode) {
+                      if (mode === 'day') {
+                        var dayToCheck = new Date(date).setHours(0, 0, 0, 0);
+
+                        for (var i = 0; i < $scope.events.length; i++) {
+                          var currentDay = new 
Date($scope.events[i].date).setHours(0, 0, 0, 0);
+
+                          if (dayToCheck === currentDay) {
+                            return $scope.events[i].status;
+                          }
+                        }
+                      }
+
+                    };
+                    break;
+
+                  case "Boolean":
+                    $scope.user.plainAttrs[schema.key].values[index] =
+                            
Boolean($scope.user.plainAttrs[schema.key].values[index]) || false;
+                    break;
+
+                }
+              };
+
+              $scope.$watch(function () {
+                return 
$scope.user.plainAttrs[$scope.schema.key].values[$scope.index];
+              }, function (newValue, oldValue) {
+                $scope.user.plainAttrs[$scope.schema.key].values = 
$scope.user.plainAttrs[$scope.schema.key].values
+                        .filter(function (n) {
+                          return (n !== undefined && n !== "");
+                        });
+              });
+            },
+            replace: true
+          };
+        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicAttribute.html
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicAttribute.html
 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicAttribute.html
deleted file mode 100644
index 9c6b1d9..0000000
--- 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicAttribute.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<div ng-switch="schema.type" >
-  <input ng-switch-when="String" class="form-control" type="text"
-         ng-model="user.plainAttrs[schema.key].values[index]"
-         ng-required="schema.mandatoryCondition" 
-         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
-  <input ng-switch-when="Encrypted" class="form-control" type="text"
-         ng-model="user.plainAttrs[schema.key].values[index]"
-         ng-required="schema.mandatoryCondition" 
-         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
-  <div ng-switch-when="Boolean">
-    <input type="checkbox" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition" 
-           ng-init="initAttribute(schema, index)" />
-  </div>
-  <input ng-switch-when="Long" class="form-control" type="number" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition"
-         ng-init="initAttribute(schema, index)" />
-  <input ng-switch-when="Double" class="form-control" type="number" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition"
-         ng-init="initAttribute(schema, index)" />
-  <p ng-switch-when="Date" class="input-group" >
-    <input type="text" class="form-control" 
-           uib-datepicker-popup="{{format}}"
-           ng-model="selectedDate"
-           ng-change="bindDateToModel(selectedDate, format)"
-           min-date="minDate" max-date="maxDate"
-           is-open="status.opened" datepicker-options="dateOptions"
-           ng-required="schema.mandatoryCondition" close-text="Close" 
ng-init="initAttribute(schema, index)"/>
-    <span class="input-group-btn">
-      <button type="button" class="btn btn-default" ng-click="open($event)"><i 
class="glyphicon glyphicon-calendar"></i></button>
-    </span>
-  </p>
-
-  <div ng-switch-when="Enum" ng-init="initAttribute(schema, index)">
-    <select class="form-control"
-            ng-model="user.plainAttrs[schema.key].values[index]"
-            ng-required="schema.mandatoryCondition">
-      <option ng-repeat="value in enumerationValues" 
value="{{value}}">{{schema.enumerationKeys[$index] || value}}</option>
-    </select>
-  </div>
-
-  <div ng-switch-when="Binary" ng-init="initAttribute(schema, index)">
-    <div enctype="multipart/form-data" accept-charset="UTF-8">
-      <input id="fileInput" type="file" 
ng-required="schema.mandatoryCondition"/>
-      <button type="button" title="Download file" class="fileButton btn 
btn-default btn-sm" ng-click="download()">
-        <i class="glyphicon glyphicon-download" ></i>
-      </button>
-      <button type="button" class="fileButton btn btn-default btn-sm" 
title="Remove file" ng-click="remove()">
-        <i class="glyphicon glyphicon-remove-sign" ></i>
-      </button>
-      <h4><span class="label label-primary" 
ng-model="userFile">{{userFile}}</span></h4>
-    </div>
-    
-  </div>
-
-  <input ng-switch-default class="form-control" type="text"
-         ng-model="user.plainAttrs[schema.key].values[index]"
-         ng-required="schema.mandatoryCondition" 
-         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
-
-</div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttribute.html
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttribute.html
 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttribute.html
new file mode 100644
index 0000000..f0d325b
--- /dev/null
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttribute.html
@@ -0,0 +1,5 @@
+<div>
+  <input class="form-control" type="text"
+         ng-model="user.derAttrs[schema.key].values[index]"
+         ng-disabled="true"/>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttributes.html
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttributes.html
 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttributes.html
index 9400877..5807553 100644
--- 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttributes.html
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicDerivedAttributes.html
@@ -1,21 +1,4 @@
-<ui-select on-select="addDerivedAttribute($item, $model)" 
on-remove="removeDerivedAttribute($item, $model)" multiple
-           ng-model="dynamicForm.selectedDerSchemas" theme="select2" 
class="attribute-ui-select">
-  <ui-select-match placeholder="Select derived 
attribute...">{{$item.key}}</ui-select-match>
-  <ui-select-choices repeat="derSchema in dynamicForm.derSchemas | 
propsFilter: {key: $select.search}">
-    <div ng-bind-html="derSchema.key | highlight: $select.search"></div>
-    <small>
-      name: {{derSchema.key}}
-      expression: {{derSchema.expression}}
-    </small>
-  </ui-select-choices>
-</ui-select>
-
-<ul class="attribute-virtual-value-container">
-  <li class="attribute-virtual-value-field" ng-repeat="selectedDerSchema in 
dynamicForm.selectedDerSchemas| filter:q as results">
-    {{selectedDerSchema.key}}
-    <input style="font-weight: normal" class="form-control" type="text" 
ng-disabled="true" ng-model="user.derAttrs[selectedDerSchema.key].values[0]"/>
-  </li>
-  <li class="attribute-virtual-value-field" ng-if="results.length == 0">
-    <strong>No derived attributes selected...</strong>
-  </li>
-</ul>
+<div id="attribute" class="form-group" ng-repeat="derSchema in 
dynamicForm.derSchemas">
+  <label for="derivedSchema.key">{{derSchema.key}} <span 
ng-if="Boolean(derSchema.mandatoryCondition) === 'true'">*</span></label>
+  <dynamic-derived-attribute schema="derSchema" user="user" 
index="0"></dynamic-derived-attribute>
+</div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
new file mode 100644
index 0000000..9c6b1d9
--- /dev/null
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
@@ -0,0 +1,58 @@
+<div ng-switch="schema.type" >
+  <input ng-switch-when="String" class="form-control" type="text"
+         ng-model="user.plainAttrs[schema.key].values[index]"
+         ng-required="schema.mandatoryCondition" 
+         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
+  <input ng-switch-when="Encrypted" class="form-control" type="text"
+         ng-model="user.plainAttrs[schema.key].values[index]"
+         ng-required="schema.mandatoryCondition" 
+         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
+  <div ng-switch-when="Boolean">
+    <input type="checkbox" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition" 
+           ng-init="initAttribute(schema, index)" />
+  </div>
+  <input ng-switch-when="Long" class="form-control" type="number" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition"
+         ng-init="initAttribute(schema, index)" />
+  <input ng-switch-when="Double" class="form-control" type="number" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition"
+         ng-init="initAttribute(schema, index)" />
+  <p ng-switch-when="Date" class="input-group" >
+    <input type="text" class="form-control" 
+           uib-datepicker-popup="{{format}}"
+           ng-model="selectedDate"
+           ng-change="bindDateToModel(selectedDate, format)"
+           min-date="minDate" max-date="maxDate"
+           is-open="status.opened" datepicker-options="dateOptions"
+           ng-required="schema.mandatoryCondition" close-text="Close" 
ng-init="initAttribute(schema, index)"/>
+    <span class="input-group-btn">
+      <button type="button" class="btn btn-default" ng-click="open($event)"><i 
class="glyphicon glyphicon-calendar"></i></button>
+    </span>
+  </p>
+
+  <div ng-switch-when="Enum" ng-init="initAttribute(schema, index)">
+    <select class="form-control"
+            ng-model="user.plainAttrs[schema.key].values[index]"
+            ng-required="schema.mandatoryCondition">
+      <option ng-repeat="value in enumerationValues" 
value="{{value}}">{{schema.enumerationKeys[$index] || value}}</option>
+    </select>
+  </div>
+
+  <div ng-switch-when="Binary" ng-init="initAttribute(schema, index)">
+    <div enctype="multipart/form-data" accept-charset="UTF-8">
+      <input id="fileInput" type="file" 
ng-required="schema.mandatoryCondition"/>
+      <button type="button" title="Download file" class="fileButton btn 
btn-default btn-sm" ng-click="download()">
+        <i class="glyphicon glyphicon-download" ></i>
+      </button>
+      <button type="button" class="fileButton btn btn-default btn-sm" 
title="Remove file" ng-click="remove()">
+        <i class="glyphicon glyphicon-remove-sign" ></i>
+      </button>
+      <h4><span class="label label-primary" 
ng-model="userFile">{{userFile}}</span></h4>
+    </div>
+    
+  </div>
+
+  <input ng-switch-default class="form-control" type="text"
+         ng-model="user.plainAttrs[schema.key].values[index]"
+         ng-required="schema.mandatoryCondition" 
+         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
+
+</div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
index 074abbd..f7759b7 100644
--- 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
@@ -1,12 +1,12 @@
 <div id="attribute" class="form-group" ng-repeat="plainSchema in 
dynamicForm.plainSchemas">
   <label for="plainSchema.key">{{plainSchema.key}} <span 
ng-if="Boolean(plainSchema.mandatoryCondition)">*</span></label>
   <div ng-if="!plainSchema.multivalue">
-    <dynamic-attribute schema="plainSchema" user="user" 
index="0"></dynamic-attribute>
+    <dynamic-plain-attribute schema="plainSchema" user="user" 
index="0"></dynamic-plain-attribute>
   </div>
 
   <div ng-if="plainSchema.multivalue">
     <div ng-repeat="field in 
dynamicForm.attributeTable[plainSchema.key].fields track by $index" 
ng-model='dynamicForm.attributeTable[plainSchema.key].fields[$index]'>
-      <dynamic-attribute schema="plainSchema" user="user" 
index="$index"></dynamic-attribute>
+      <dynamic-plain-attribute schema="plainSchema" user="user" 
index="$index"></dynamic-plain-attribute>
       <span>
         <button class="btn btn-default btn-sm minus" ng-if="$index > 0" 
type="button" ng-click="removeAttributeField(plainSchema.key, $index)">
           <i class="glyphicon glyphicon-minus" title="Remove value"></i>

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicVirtualAttribute.html
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicVirtualAttribute.html
 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicVirtualAttribute.html
new file mode 100644
index 0000000..9c6b1d9
--- /dev/null
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicVirtualAttribute.html
@@ -0,0 +1,58 @@
+<div ng-switch="schema.type" >
+  <input ng-switch-when="String" class="form-control" type="text"
+         ng-model="user.plainAttrs[schema.key].values[index]"
+         ng-required="schema.mandatoryCondition" 
+         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
+  <input ng-switch-when="Encrypted" class="form-control" type="text"
+         ng-model="user.plainAttrs[schema.key].values[index]"
+         ng-required="schema.mandatoryCondition" 
+         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
+  <div ng-switch-when="Boolean">
+    <input type="checkbox" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition" 
+           ng-init="initAttribute(schema, index)" />
+  </div>
+  <input ng-switch-when="Long" class="form-control" type="number" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition"
+         ng-init="initAttribute(schema, index)" />
+  <input ng-switch-when="Double" class="form-control" type="number" 
ng-model="user.plainAttrs[schema.key].values[index]" 
ng-required="schema.mandatoryCondition"
+         ng-init="initAttribute(schema, index)" />
+  <p ng-switch-when="Date" class="input-group" >
+    <input type="text" class="form-control" 
+           uib-datepicker-popup="{{format}}"
+           ng-model="selectedDate"
+           ng-change="bindDateToModel(selectedDate, format)"
+           min-date="minDate" max-date="maxDate"
+           is-open="status.opened" datepicker-options="dateOptions"
+           ng-required="schema.mandatoryCondition" close-text="Close" 
ng-init="initAttribute(schema, index)"/>
+    <span class="input-group-btn">
+      <button type="button" class="btn btn-default" ng-click="open($event)"><i 
class="glyphicon glyphicon-calendar"></i></button>
+    </span>
+  </p>
+
+  <div ng-switch-when="Enum" ng-init="initAttribute(schema, index)">
+    <select class="form-control"
+            ng-model="user.plainAttrs[schema.key].values[index]"
+            ng-required="schema.mandatoryCondition">
+      <option ng-repeat="value in enumerationValues" 
value="{{value}}">{{schema.enumerationKeys[$index] || value}}</option>
+    </select>
+  </div>
+
+  <div ng-switch-when="Binary" ng-init="initAttribute(schema, index)">
+    <div enctype="multipart/form-data" accept-charset="UTF-8">
+      <input id="fileInput" type="file" 
ng-required="schema.mandatoryCondition"/>
+      <button type="button" title="Download file" class="fileButton btn 
btn-default btn-sm" ng-click="download()">
+        <i class="glyphicon glyphicon-download" ></i>
+      </button>
+      <button type="button" class="fileButton btn btn-default btn-sm" 
title="Remove file" ng-click="remove()">
+        <i class="glyphicon glyphicon-remove-sign" ></i>
+      </button>
+      <h4><span class="label label-primary" 
ng-model="userFile">{{userFile}}</span></h4>
+    </div>
+    
+  </div>
+
+  <input ng-switch-default class="form-control" type="text"
+         ng-model="user.plainAttrs[schema.key].values[index]"
+         ng-required="schema.mandatoryCondition" 
+         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
+
+</div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/4d2cd5f9/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
----------------------------------------------------------------------
diff --git 
a/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html 
b/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
index 3048010..13d50a4 100644
--- 
a/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
+++ 
b/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
@@ -40,8 +40,8 @@ under the License.
               <a ui-sref-active="active" ui-sref=".credentials" class="btn 
btn-default">Credentials</a>
               <a ui-sref-active="active" ui-sref=".groups" class="btn 
btn-default">Groups</a>
               <a ui-sref-active="active" ui-sref=".plainSchemas" class="btn 
btn-default">Plain Schemas</a>
-              <!--<a ui-sref-active="active" ui-sref=".derivedSchemas" 
class="btn btn-default">Derived Schemas</a>
-              <a ui-sref-active="active" ui-sref=".virtualSchemas" class="btn 
btn-default">Virtual Schemas</a>-->              
+              <a ui-sref-active="active" ui-sref=".derivedSchemas" class="btn 
btn-default">Derived Schemas</a>
+              <a ui-sref-active="active" ui-sref=".virtualSchemas" class="btn 
btn-default">Virtual Schemas</a>              
               <a ui-sref-active="active" ui-sref=".resources" class="btn 
btn-default">Resources</a>
               <a ui-sref-active="active" ui-sref=".finish" class="btn 
btn-default">Finish</a>
             </div>

Reply via email to