@@ -34,6 +34,86 @@ object ToolboxOps {
<xforms:alert ref="$fr-resources/detail/labels/alert"/>
</template>

// Insert a new control in a cell
def insertNewControl(doc: NodeInfo, binding: NodeInfo) {

ensureEmptyTd(doc) match {
case Some(gridTd) =>

val newControlId = nextId(doc, "control")
val newControlName = "control-" + newControlId

// Insert control template
val newControlElement: NodeInfo =
binding \ "*:metadata" \ "*:template" \ * match {
case Seq(template, _*) =>
// There is a specific template available
insert(into = gridTd, origin = template).head
case _ =>
// No specific, create simple element with LHHA

// Try to find the binding QName (for now takes the first CSS rule and assume the form foo|bar)
val firstElementCSSName = (binding \@ "element" stringValue) split "," head
val elementQName = firstElementCSSName.replace('|', ':')

val controlElement = insert(into = gridTd, origin = elementInfo(resolveQName(binding, elementQName))).head
insert(into = controlElement, origin = lhhaTemplate \ *)
controlElement
}

// Set default pointer to resources if there is an xforms:alert
setvalue(newControlElement \ "*:alert" \@ "ref", "$fr-resources/detail/labels/alert")

// Adjust bindings on newly inserted control
renameControlByElement(newControlElement, newControlName)

// Get control type
// TODO: for now assume a literal 'xs:' prefix (should resolve namespace)
val controlType = binding \ "*:metadata" \ "*:datatype" match {
case Seq(datatype, _*) => datatype.stringValue
case _ => "xs:string"
}

// Data holder may contain file attributes
val dataHolder =
if (Set("xs:anyURI", "xforms:anyURI")(controlType))
elementInfo(newControlName, Seq("filename", "mediatype", "size") map (attributeInfo(_)))
else
elementInfo(newControlName)

val lhhaNames = newControlElement \ * filter (e => Set("label", "help", "hint", "alert")(localname(e))) map (localname(_))
val resourceHolder = elementInfo(newControlName, lhhaNames map (elementInfo(_)))

// Insert data and resource holders
insertHolders(
newControlElement,
dataHolder,
resourceHolder,
precedingControlNameInSectionForControl(newControlElement)
)

// Insert the bind element
val bind = ensureBinds(doc, findContainerNames(gridTd) :+ newControlName, isCustomInstance)

// Make sure there is a @bind instead of a @ref on the control
delete(newControlElement \@ "ref")
ensureAttribute(newControlElement, "bind", bind \@ "id" stringValue)

// Set bind type if needed
if (controlType != "xs:string")
insert(into = bind, origin = attributeInfo("type", controlType))

debugDumpDocument("insert new control", doc)

// Open label editor for newly inserted control -->
// TODO
// <xforms:toggle case="fr-inplace-fb-control-label-control-edit"/>
// <xforms:setfocus control="fr-inplace-fb-control-label-control-input"/>

case _ => // no empty td found/created so NOP
}
}

// Insert a new grid
def insertNewGrid(doc: NodeInfo) {

@@ -192,7 +272,12 @@ object ToolboxOps {
val newGridElement = insert(into = currentTdGrandParentContainer, after = currentTdGrid, origin = gridTemplate).head

// Insert instance holder (but no resource holders)
insertHolders(newGridElement, elementInfo(newGridName), Seq(), precedingControlNameInSectionForTd(currentTdGrid \\ "*:td" last, includeSelf = true))
insertHolders(
newGridElement,
elementInfo(newGridName),
Seq(),
precedingControlNameInSectionForGrid(currentTdGrid, true)
)

// Make sure binds are created
ensureBinds(doc, findContainerNames(newGridElement) :+ newGridName, isCustomInstance)
@@ -206,81 +291,6 @@ object ToolboxOps {
}
}

// Insert a new control in a cell
def insertNewControl(doc: NodeInfo, binding: NodeInfo) {

ensureEmptyTd(doc) match {
case Some(gridTd) =>

val newControlId = nextId(doc, "control")
val newControlName = "control-" + newControlId

// Insert control template
val newControlElement: NodeInfo =
binding \ "*:metadata" \ "*:template" \ * match {
case Seq(template, _*) =>
// There is a specific template available
insert(into = gridTd, origin = template).head
case _ =>
// No specific, create simple element with LHHA

// Try to find the binding QName (for now takes the first CSS rule and assume the form foo|bar)
val firstElementCSSName = (binding \@ "element" getStringValue) split "," head
val elementQName = firstElementCSSName.replace('|', ':')

val controlElement = insert(into = gridTd, origin = elementInfo(resolveQName(binding, elementQName))).head
insert(into = controlElement, origin = lhhaTemplate \ *)
controlElement
}

// Set default pointer to resources if there is an xforms:alert
setvalue(newControlElement \ "*:alert" \@ "ref", "$fr-resources/detail/labels/alert")

// Adjust bindings on newly inserted control
renameControlByElement(newControlElement, newControlName)

// Get control type
// TODO: for now assume a literal 'xs:' prefix (should resolve namespace)
val controlType = binding \ "*:metadata" \ "*:datatype" match {
case Seq(datatype, _*) => datatype.getStringValue
case _ => "xs:string"
}

// Data holder may contain file attributes
val dataHolder =
if (Set("xs:anyURI", "xforms:anyURI")(controlType))
elementInfo(newControlName, Seq("filename", "mediatype", "size") map (attributeInfo(_)))
else
elementInfo(newControlName)

val lhhaNames = newControlElement \ * filter (e => Set("label", "help", "hint", "alert")(localname(e))) map (localname(_))
val resourceHolder = elementInfo(newControlName, lhhaNames map (elementInfo(_)))

// Insert data and resource holders
insertHolders(newControlElement, dataHolder, resourceHolder, precedingControlNameInSection(newControlElement))

// Insert the bind element
val bind = ensureBinds(doc, findContainerNames(gridTd) :+ newControlName, isCustomInstance)

// Make sure there is a @bind instead of a @ref on the control
delete(newControlElement \@ "ref")
ensureAttribute(newControlElement, "bind", bind \@ "id" getStringValue)

// Set bind type if needed
if (controlType != "xs:string")
insert(into = bind, origin = attributeInfo("type", controlType))

debugDumpDocument("insert new control", doc)

// Open label editor for newly inserted control -->
// TODO
// <xforms:toggle case="fr-inplace-fb-control-label-control-edit"/>
// <xforms:setfocus control="fr-inplace-fb-control-label-control-input"/>

case _ => // no empty td found/created so NOP
}
}

// Copy control to the clipboard
def copyToClipboard(td: NodeInfo) {
val name = getControlName(td \ * head)
@@ -348,14 +358,14 @@ object ToolboxOps {
insertHolders(
newControlElement,
xvc \ "holder" \ * head,
xvc \ "resources" \ "resource" map (r => (r \@ "*:lang" getStringValue, r \ * head)),
precedingControlNameInSection(newControlElement)
xvc \ "resources" \ "resource" map (r => (r \@ "*:lang" stringValue, r \ * head)),
precedingControlNameInSectionForControl(newControlElement)
)

// Create the bind and copy all attributes and content
val bind = ensureBinds(gridTd, findContainerNames(gridTd) :+ controlName, isCustomInstance)
(xvc \ "bind" \ * headOption) foreach { xvcBind =>
insert(into = bind, origin = (xvcBind \@ *) ++ (xvcBind \ *))
insert(into = bind, origin = (xvcBind \@ @*) ++ (xvcBind \ *))
}
}
}
@@ -162,10 +162,10 @@ object FormRunner {
filter (p =>
(p \ * isEmpty) || // No constraint on the permission, so it is automatically satisfied
(p \ "user-role" forall (r => // If we have user-role constraints, they must all pass
(r \@ "any-of" getStringValue) split "\\s+" // Constraint is satisfied if user has at least one of the roles
(r \@ "any-of" stringValue) split "\\s+" // Constraint is satisfied if user has at least one of the roles
map (_.replace("%20", " ")) // Unescape internal spaces as the roles used in Liferay are user-facing labels that can contain space (see also permissions.xbl)
exists (request.isUserInRole(_)))))
flatMap (p => (p \@ "operations" getStringValue) split "\\s+") // For the permissions that passed, return the list operations
flatMap (p => (p \@ "operations" stringValue) split "\\s+") // For the permissions that passed, return the list operations
distinct // Remove duplicate operations
)
}) asJava
@@ -286,7 +286,7 @@ class FormBuilderFunctionsTest extends DocumentTestBase with AssertionsForJUnit
assert(formResourcesRoot(doc) \ "resource" \ "control-2" nonEmpty)

val templateHolder = templateRoot(doc, "grid-1").get \ "control-2" headOption

assert(templateHolder.isDefined)
assert(templateHolder.get precedingSibling * isEmpty)
assert(name(templateHolder.get.parent.get) === "grid-1")
@@ -319,6 +319,52 @@ class FormBuilderFunctionsTest extends DocumentTestBase with AssertionsForJUnit
yield assert(getRowCells(trs(index)) === expected)
}

@Test def testPrecedingNameForControl() {

val section: NodeInfo =
<section>
<grid>
<tr><td><input id="control-11-control"/></td></tr>
</grid>
<grid id="grid-2-grid">
<tr><td><input id="control-21-control"/></td></tr>
</grid>
<grid>
<tr><td><input id="control-31-control"/></td></tr>
</grid>
</section>

val controls = section \\ "*:td" \ * filter (_ \@ "id" nonEmpty)

val expected = Seq(None, Some("grid-2"), Some("grid-2"))
val actual = controls map (precedingControlNameInSectionForControl(_))

assert(actual === expected)
}

@Test def testPrecedingNameForGrid() {

val section: NodeInfo =
<section>
<grid>
<tr><td><input id="control-11-control"/></td></tr>
</grid>
<grid id="grid-2-grid">
<tr><td><input id="control-21-control"/></td></tr>
</grid>
<grid>
<tr><td><input id="control-31-control"/></td></tr>
</grid>
</section>

val grids = section \\ "*:grid"

val expected = Seq(Some("control-11"), Some("grid-2"), Some("control-31"))
val actual = grids map (precedingControlNameInSectionForGrid(_, true))

assert(actual === expected)
}

// @Test def insertHolders() {
//
// }