isundil 4 жил өмнө
parent
commit
51c4aa5b97

+ 111 - 6
public/javascripts/entity.js

@@ -1,9 +1,16 @@
-((dn, inputs, ldifOutput) => {
+((dn, schema, inputs, ldifOutput, classContainer, addClassSelect, addClassBtn) => {
 	let changes = [],
-		multipleValues = {};
+		multipleValues = {},
+		maxInputId = 0,
+		newClasses = {},
+		attributeCount = {};
 
 	function ComputeChanges() {
+		let classChanges = [];
 		let actualChanges = [];
+
+		for (let i in newClasses)
+			classChanges.push(`add: objectClass\nobjectClass: ${i}`);
 		for (let i = 0; i < changes.length; ++i) {
 			let ch = changes[i];
 			if (!ch)
@@ -21,10 +28,10 @@
 					actualChanges.push(`add: ${ch.attrName}\n${ch.attrName}: ${ch.newValue}`);
 			}
 		}
-		if (!actualChanges.length)
+		let totalChanges = classChanges.concat(actualChanges.sort((a, b) => b.localeCompare(a)));
+		if (!totalChanges.length)
 			return "";
-		actualChanges.sort((a, b) => b.localeCompare(a));
-		return `dn: ${dn}\nchangetype: modify\n` + actualChanges.join("\n-\n");
+		return `dn: ${dn}\nchangetype: modify\n` + totalChanges.join("\n-\n");
 	}
 	function UpdateLdif() {
 		ldifOutput.textContent = ComputeChanges();
@@ -43,10 +50,108 @@
 		UpdateLdif();
 	}
 
+	let prevItem = null; // input field
+	function addAddButton(item) { // item is the input field
+		let addButton = document.createElement("a");
+		addButton.innerText = "Add";
+		addButton.src = "#";
+		addButton.parentInput = item;
+		addButton.addEventListener("click", (e) => {
+			e.preventDefault();
+			let line = addButton.parentInput.parentNode.parentNode;
+			let copy = line.cloneNode(true);
+			line.parentNode.insertBefore(copy, line);
+			let input = copy.querySelectorAll("label > input")[0];
+			input.value = "";
+			input.dataset.initialValue = "";
+			input.addEventListener("change", e => onValueChanged(e.currentTarget));
+			input.dataset.inputId = ++maxInputId;
+			let button = copy.querySelectorAll("label > span > a")[0];
+			button.parentNode.removeChild(button);
+		});
+		item.parentNode.children[0].appendChild(addButton);
+	}
+	function manageDuplicateAttributes(li, input) {
+		attributeCount[input.dataset.attributeName] = (attributeCount[input.dataset.attributeName] || {});
+		attributeCount[input.dataset.attributeName][input.dataset.klass] = true;
+		if (Object.keys(attributeCount[input.dataset.attributeName]).length > 1)
+			li.style.display = "none";
+	}
 	inputs.forEach(i => {
+		maxInputId = Math.max(i.dataset.inputId, maxInputId);
 		let attrName = i.dataset.attributeName;
 		if ((i.dataset.initialValue || "").length)
 			multipleValues[attrName] = (multipleValues[attrName] || 0) + 1;
 		i.addEventListener("change", e => onValueChanged(e.currentTarget))
+		if (prevItem != null &&
+			prevItem.dataset.attributeName !== i.dataset.attributeName) {
+			addAddButton(prevItem);
+		}
+		prevItem = i;
+		manageDuplicateAttributes(i.parentElement.parentElement, i);
+	});
+	prevItem && addAddButton(prevItem);
+
+	function CreateClassAttributeDom(className, name, init, isMandatory) {
+		let li = document.createElement("li");
+		let label = document.createElement("label");
+		label.classList.add("LDAPAttribute");
+		li.appendChild(label);
+		let span = document.createElement("span");
+		if (isMandatory)
+			span.classList.add("mandatory");
+		span.innerText = name;
+		let input = document.createElement("input");
+		input.type = "text";
+		input.value = init;
+		input.dataset.initialValue = init;
+		input.dataset.inputId = ++maxInputId;
+		input.dataset.name = name;
+		input.dataset.klass = className;
+		input.dataset.attributeName = name;
+		input.required = isMandatory;
+		input.addEventListener("change", e => onValueChanged(e.currentTarget))
+		label.appendChild(span);
+		label.appendChild(input);
+		addAddButton(input);
+		manageDuplicateAttributes(li, input);
+		return li;
+	}
+
+	function CreateClassDom(schema, className) {
+		let classDom = document.createElement("fieldset");
+		classDom.classList.add("LDAPClass");
+		let legend = document.createElement("legend");
+		let legendInner = document.createElement("h3");
+		legendInner.innerText = className +' (' +schema["type"] +')';
+		legend.appendChild(legendInner);
+		classDom.appendChild(legend);
+		let list = document.createElement("ul");
+		for (let i of schema["must"])
+			list.appendChild(CreateClassAttributeDom(className, i, "", true));
+		for (let i of schema["may"])
+			list.appendChild(CreateClassAttributeDom(className, i, "", false));
+		classDom.appendChild(list);
+		return classDom;
+	}
+
+	addClassBtn.addEventListener("click", () => {
+		let targetClass = addClassSelect.value;
+		if (!schema[targetClass])
+			return;
+		for (let i of addClassSelect.children) {
+			if (i.value === targetClass) {
+				addClassSelect.removeChild(i);
+				break;
+			}
+		}
+		newClasses[targetClass] = true;
+		let dom = CreateClassDom(schema[targetClass], targetClass);
+		classContainer.appendChild(dom);
+		UpdateLdif();
 	});
-})(window['dn'], document.querySelectorAll(".LDAPAttribute > input"), document.getElementById("ldifOutput"));
+})(
+	window['dn'], window['schema'],
+	document.querySelectorAll(".LDAPAttribute > input"), document.getElementById("ldifOutput"),
+	document.getElementById("classContainer"),
+	document.getElementById("addClassSelect"), document.getElementById("addClassBtn"));

+ 53 - 15
routes/entity.ts

@@ -15,6 +15,35 @@ function LDAPEntryToAttributes(entry: Map<string, Array<string>>): any {
     return result;
 }
 
+function getType(schema: Map<string, LDAPSchemaObjectClass>, klass: string): string {
+    switch (schema.get(klass)?.GetType()) {
+        case ClassType.eAbstract:
+            return "abstract";
+        case ClassType.eAuxiliary:
+            return "auxiliary";
+        case ClassType.eStructural:
+            return "structural";
+    }
+    return "Unknown";
+}
+
+function StructifySchema(schema: Map<string, LDAPSchemaObjectClass>): any {
+    let result: any = {};
+    for (let [key, oc] of schema) {
+        if (oc.GetType() === ClassType.eAbstract)
+            continue;
+        let obj = {
+            may: oc.ListMayAttributes().filter(i => i.toLowerCase() !== 'objectclass'),
+            must: oc.ListMustAttributes().filter(i => i.toLowerCase() !== 'objectclass'),
+            structural: oc.GetType() === ClassType.eStructural,
+            auxiliary: oc.GetType() === ClassType.eAuxiliary,
+            type: getType(schema, key)
+        };
+        result[key] = obj;
+	}
+    return result;
+}
+
 class AttributesByClasses {
     public constructor(entry: Map<string, string[]>, classes: Map<string, LDAPSchemaObjectClass>) {
         for (let eClass of entry.get("objectClass") || []) {
@@ -23,12 +52,13 @@ class AttributesByClasses {
             cl && this.fObjectClasses.push(cl);
 		}
         for (let [i, j] of entry) {
+            if (i.toLowerCase() == 'objectclass')
+                continue;
             let found = false;
             for (let oc of this.fObjectClasses) {
                 if (oc.HasAttribute(i)) {
                     this.fEntries.get(oc.GetName())?.set(i, j);
                     found = true;
-                    break;
 				}
             }
             if (!found)
@@ -59,9 +89,8 @@ class AttributesByClasses {
         let result: any = {};
         for (let [ocKey, vals] of this.fEntries) {
             let classContent: any = {};
-            for (let [i, j] of vals) {
+            for (let [i, j] of vals)
                 classContent[i] = j.length ? j : [""];
-            }
             result[ocKey] = classContent;
         }
         return result;
@@ -69,10 +98,14 @@ class AttributesByClasses {
 
     public GetObjectClasses(): any {
         let result: any = [];
-        for (let [ocKey, val] of this.fEntries)
+        for (let [ocKey, _] of this.fEntries)
             result.push(ocKey);
         result.sort();
         return result;
+    }
+
+    public ClassExists(className: string): boolean {
+        return this.fEntries.has(className);
 	}
 
     private fObjectClasses: LDAPSchemaObjectClass[] = new Array();
@@ -95,18 +128,23 @@ router.get('/:dn', (req: express.Request, res: express.Response) => {
                 dn: dn || req.params.dn,
                 attributes: classes.ToMap(),
                 classes: classes.GetObjectClasses(),
-                getType: (klass: string): string => {
-                    switch (schema.get(klass)?.GetType()) {
-                        case ClassType.eAbstract:
-                            return "abstract";
-                        case ClassType.eAuxiliary:
-                            return "auxiliary";
-                        case ClassType.eStructural:
-                            return "structural";
+                getType: (klass: string) => getType(schema, klass),
+                isMandatory: (attr: string): boolean => classes.IsMandatoryAttr(attr),
+                schema: StructifySchema(schema),
+                getUnusedClass: (): string[] => {
+                    let result = [];
+                    for (let [key, klass] of schema) {
+                        if (!classes.ClassExists(key) && klass.GetType() !== ClassType.eAbstract)
+                            result.push({ key: key, klass: klass });
                     }
-                    return "Unknown";
-                },
-                isMandatory: (attr: string): boolean => classes.IsMandatoryAttr(attr)
+                    return result.sort((a, b) => {
+                        if (a.klass.GetType() === ClassType.eStructural && b.klass.GetType() === ClassType.eAuxiliary)
+                            return -1;
+                        if (a.klass.GetType() === ClassType.eAuxiliary && b.klass.GetType() === ClassType.eStructural)
+                            return 1;
+                        return a.key.localeCompare(b.key);
+                    }).map(a => a.key);
+                }
             });
     });
 });

+ 12 - 0
src/LDAPSchema.ts

@@ -134,6 +134,18 @@ export class LDAPSchemaObjectClass extends LDAPSchemaItem {
 		return this.fMustAttributes.has(attr);
 	}
 
+	public ListMayAttributes(): string[] {
+		let result = new Array();
+		for (let [key, _] of this.fMayAttributes) result.push(key);
+		return result;
+	}
+
+	public ListMustAttributes(): string[] {
+		let result = new Array();
+		for (let [key, _] of this.fMustAttributes) result.push(key);
+		return result;
+	}
+
 	public ListAttributes(): string[] {
 		let result = new Array();
 		for (let [key, _] of this.fMustAttributes) result.push(key);

+ 21 - 14
views/entity.pug

@@ -3,22 +3,29 @@ extends layout
 block content
   script.
     window['dn']="#{dn}";
+    window['schema']=!{JSON.stringify(schema)};
   div(id="entity-page",class="page",data-dn=dn)
     h1=dn
-    -id=0
-    for className in classes
-      fieldset(class="LDAPClass")
-        legend
-          h3=className+" (" +getType(className)+")"
-        ul
-          each valArr, key in attributes[className]
-            for val in valArr
-              li
-                label(class="LDAPAttribute")
-                  -mandatory=isMandatory(key)
-                  span(class=mandatory ? "mandatory": "")=key
-                  input(type="text",value=val,data-initial-value=val,data-input-id=id,data-attribute-name=key,required=mandatory)
-                  -id++
+    div(id="classContainer")
+      -id=0
+      for className in classes
+        fieldset(class="LDAPClass")
+          legend
+            h3=className+" (" +getType(className)+")"
+          ul
+            each valArr, key in attributes[className]
+              for val in valArr
+                li
+                  label(class="LDAPAttribute")
+                    -mandatory=isMandatory(key)
+                    span(class=mandatory ? "mandatory": "")=key
+                    input(type="text",value=val,data-initial-value=val,data-input-id=id,data-attribute-name=key,required=mandatory,data-klass=className)
+                    -id++
+    div
+      select(id="addClassSelect")
+        for unusedClass in getUnusedClass()
+          option=unusedClass
+      input(type="submit",value="Add Class",id="addClassBtn")
     fieldset(class="changes")
       textarea(id="ldifOutput",readonly)
       input(type="button",value="Submit")