Add an attribute to an element in an existing XML using Xquery - xquery

I need to add an attribute to an element of my response XML using XQuery.
Take the below XML as input,
<xyz:RootNode xmlns:abc="url1" xmlns:xyz="url2">
<abc:OtherNodes/>
<abc:messageHeader att1="val1" att2="val2">
<abc:childNodes/>
</abc:messageHeader>
<abc:OtherNodes/>
</xyz:RootNode>
Need an Xquery that add one more attribute newAtt with value newVal and give the result as,
<xyz:RootNode xmlns:abc="url1" xmlns:xyz="url2">
<abc:OtherNodes/>
<abc:messageHeader att1="val1" att2="val2" newAtt="newVal">
<abc:childNodes/>
</abc:messageHeader>
<abc:OtherNodes>
</xyz:RootNode>
Each time the number of attributes of message header may change. So the query should add a new attribute along with all the existing attributes and return the whole document.

Try the following:
xquery version "3.0";
module namespace foo="http://exist-db.org/apps/ns/foo";
declare function foo:process-node($node as node()?, $model as map()) {
if ($node) then
typeswitch($node)
case text() return $node
case element(messageHeader) return foo:messageHeader($node, $model)
default return element { $node/name() }
{ $node/#*, foo:recurse($node, $model) }
else ()
};
declare function foo:recurse($node as node()?, $model as map()) as item()* {
if ($node)
then
for $cnode in $node/node()
return foo:process-node($cnode, $model)
else ()
};
declare function foo:messageHeader($node as node(), $model as map()) {
element { $node/name() }
{ $node/#*,
attribute { 'newAtt' } { 'newVal' },
foo:recurse($node, $model)
}
};
You then call foo:process-node on the RootNode

You can always re-use the wheel that is there for such things, XSLT. Especially if you want to minimise risk in your code.
eXist supports XSL Transformations and here is an example of how to run an XSLT transformation that does the job that you want:
xquery version "3.0";
declare function local:add-attribute($input as node()?, $attributeName as xs:string, $attributeValue as xs:string?) {
let $xslt := <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template xmlns:abc="url1" match="abc:messageHeader">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="{$attributeName}">{$attributeValue}</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
return transform:transform($input, $xslt, ())
};
let $input := <xyz:RootNode xmlns:abc="url1" xmlns:xyz="url2">
<abc:OtherNodes/>
<abc:messageHeader att1="val1" att2="val2">
<abc:childNodes/>
</abc:messageHeader>
<abc:OtherNodes/>
</xyz:RootNode>
return local:add-attribute($input, "hey", "bam")
You can also make use of the $parameters attribute if you want to bring your XSLT out into its own file. Will make things even more testable and modular.

Related

Write expression inside xml comment

I'd like to put xml comment in the output and put some expression inside the comment. How can I do that in xquery? If I have
<!-- {$var} -->
it is inserted literally, but I'd like to have a comment tag on the output with the value of $var
XQuery has a comment constructor, for example:
<test>
{
let $x := 'hello world!'
return comment {$x}
}
</test>
yields:
<?xml version="1.0" encoding="UTF-8"?>
<test><!--hello world!--></test>
Would something like this work?
for $x in /* return <x><![CDATA[<!--]]>{$x}<![CDATA[-->]]> </x>
input
<test>hello comment</test>
output
<!--hello comment-->

Add space after comma when publishing an array meteor

When I publish and print <p>{{profile.classes}}</p> I get English,Maths,Science. I would like it to print out with a space after the class English, Maths, Science.
I imagine there is a simple way to do this? I'm using, autoform and simpleschema just incase that's relevant.
Answer from #ThiagoSckianta is almost correct except, you need to replace using regex to replace all occurrences. See this link.
Template.NameOfYourController.helpers({
profileClasses: function () {
return (this.profile && this.profile.classes) ?
this.profile.classes.replace(/,/g, ', ') : '';
}
});
Then, in your html:
<p>{{profileClasses}}</p>
Update:
If profile.classes is an array, then you do 'toString` before calling replace like this
Template.NameOfYourController.helpers({
profileClasses: function () {
var classes = (this.profile && this.profile.classes) ? this.profile.classes.toString() : '';
return classes.replace(/,/g, ', ');
}
});
You should implement a helper function.
Go to the .js file of your page, where the controller is defined, and paste the code below. Remember to substitute the name of your controller.
Template.NameOfYourController.helpers({
profileClasses: function () {
// Avoid a ReferenceError in case your subscription is not ready or the field is not set
return (this.profile && this.profile.classes) ?
this.profile.classes.replace(/,/g, ', ') : '';
}
});
Then, in your .html file, write:
<p>{{profileClasses}}</p>
Another option is to pass a parameter to your helper function:
Template.NameOfYourController.helpers({
formatList: function (list) {
return list ? list.replace(/,/g, ', ') : '';
}
});
Then, in your html:
<p>{{formatList profile.classes}}</p>
The Meteor tutorial is an interesting starting point to learn this.
You can find more details about templates and helpers in this session of the docs

What's the best/safest/most effective way of returning HTML from a Meteor helper?

I'm trying to return HTML from a helper function after certain logical conditions are met. However, using the Spacebars.SafeString() function doesn't seem to be working and I gather that using this method to return HTML is unsafe and is prone to code injection from external sources.
For example, returning this HTML:
<object data="/rating/what.svg" width="20" height="20" type="image/svg+xml"></object>
Set up like this:
Spacebars.SafeString("<object data=" + "/rating/what.svg" + "width='20' height='20' type=" + "image/svg+xml" + "></object>");
Can anyone guide me to the best way to return HTML from a helper and how to do such a task? Couldn't find a definitive answer anywhere else.
First of all, if your requirements allow it, don't return HTML at all, just use a template and populate it with a data context, for example:
in your template:
<template name="someHtml">
<object data="/rating/{{dynamicName}}.svg" width="20" height="20" type="image/svg+xml"></object>
</template>
in the corresponding helper:
Template.someHtml.helpers({
dynamicName: function() {
return 'what'; // obviously, you generate this with some code
}
})
But, if you truly must use html content to be printed, you can use one of the two most popular sanitization packages, either djedi:sanitize-html-client or vazco:universe-html-purifier
With the first:
cleanHtml = sanitizeHtml(dirtyHtml, {
allowedTags: [ 'b', 'i', 'em', 'strong', 'a' ],
allowedAttributes: {
'a': [ 'href' ]
}
});
and with the latter:
cleanHtml = UniHTML.purify(dirtyHtml, {
withoutTags: ['b', 'img'],
noFormatting: true
});
And then of course you include the return value of these in your template with either triple braces so that the HTML is not escaped.
You are correct in that Spacebars.SafeString() can return unsafe code. Your best bet is to strip out bad tags or injections that you do not want and either use Spacebars.SafeString() or triple brace syntax like below:
<div>
{{{htmlHelper}}}
</div>
htmlHelper: function() {
return "<img src=..."
}

React + Meteor keyed object warning

How do I get rid of this warning? If I delete task.created there is no warning. Can't figure this one out. See my code example below:
Warning: Any use of a keyed object should be wrapped in React.addons.createFragment(object) before being passed as a child.
ShowAllPosts = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
// This function knows how to listen to Meteor's reactive data sources,
// such as collection queries
return {
// Returns an array with all items in the collection
tweets: Posts.find().fetch().reverse()
}
},
render() {
var showHTML = this.data.posts.map(function (task) {
return (
<div key={task._id}>
<img className="profile-pic" src="images/puppy.jpeg" />
{task.content}
<br />
Date: {task.created}, Get Link, id: {task._id}
<hr />
</div>
);
});
return (
<ul>
<hr />
{/* Access the data from getMeteorData() on this.data */}
{ showHTML }
</ul>
);
}
});
I assume that task.created is a Date object.
React isn't casting dates to strings automatically, so you need to write something like {task.created.toString()} or use package like moment to format your date.

How to customize the Document List in Alfresco?

In the repository there be will different document lists.i.e there will be Data dictionary, user homes,Guest homes etc. when I change the view to "detailed view", it displays Favorite, like, comments links. where will I have to modify if I dont want to show them. Can you tell in which file i have to comment the code for not displaying those links. Thank you in Advance.
I wanted a "modular" answer to this question, this answer is to show how I handled this issue.
Context: Alfresco 4.2.f, project Maven from the org.alfresco.maven.archetype:alfresco-amp-archetype:1.1.1 archetype, I put everything in the embedded JAR when possible.
Create a module extension for share (see this blog for more details). Here is my extension file:
src/main/resources/alfresco/site-data/extensions/my-custom-extension.xml
<extension>
<modules>
<module>
<id>Main module of my custom extension</id>
<version>${project.version}</version>
<auto-deploy>true</auto-deploy>
<customizations>
<customization>
<!-- Order matters here! target before source, always! -->
<targetPackageRoot>org.alfresco</targetPackageRoot>
<sourcePackageRoot>my-custom.main</sourcePackageRoot>
</customization>
</customizations>
</module>
</modules>
</extension>
In the documentlibrary component of your module package, create this FTL in order to declare a javascript:
src/main/resources/alfresco/site-webscripts/my-custom/main/components/documentlibrary/documentlist-v2.get.html.ftl
<#-- Add a Javascript declaration -->
<#markup id="my-custom-js" target="js" action="after">
<#script type="text/javascript" group="documentlibrary"
src="${url.context}/res/my-custom/main/components/documentlibrary/documentlist.js"/>
</#>
In the resources (META-INF), under documentlibrary component, create the Javascript:
src/main/resources/META-INF/my-custom/main/components/documentlibrary/documentlist.js
YAHOO.lang.augmentObject(Alfresco.DocumentList.prototype, {
// Possible values: i18nLabel, lockBanner, syncFailed, syncTransientError
// date, size, name, version, description, tags, categories
myCustomDisabledRenderers: ["description", "version", "tags"],
// Possible values: favourites, likes, comments, quickShare
myCustomDisabledSocials: ["favourites", "comments", "likes", "quickShare"],
myCustomIsSocialDisabled: function(propertyName) {
return Alfresco.util.arrayContains(
this.myCustomDisabledSocials, propertyName);
},
myCustomIsRendererDisabled: function(propertyName) {
if (Alfresco.util.arrayContains(
this.myCustomDisabledRenderers, propertyName)) {
return true;
}
// Disable the social renderer when all the social features are
// disabled
if (propertyName === "social" && this.myCustomDisabledSocials.length == 4) {
return true;
}
return false;
},
/** Helper function to disable socials
* propertyName must be one of "favourites", "comments", "likes", "quickShare"
*/
myCustomDisableSocial: function(propertyName) {
if (!Alfresco.util.arrayContains(
this.myCustomDisabledSocials, propertyName)) {
this.myCustomDisabledSocials.push(propertyName);
}
},
// Custom registerRenderer for social features, originally defined in:
// webapps/share/components/documentlibrary/documentlist.js:2134
myCustomSocialRegisterRenderer: function(record) {
var jsNode = record.jsNode;
var html = "";
// Current usage of the separator variable allow to change the order
// of the different social features (the 'if' blocks below) without
// changing their content
var separator = "";
/* Favourite / Likes / Comments */
if (!this.myCustomIsSocialDisabled("favourites")) {
html += '<span class="item item-social' + separator + '">' +
Alfresco.DocumentList.generateFavourite(this, record) +
'</span>';
separator = " item-separator";
}
if (!this.myCustomIsSocialDisabled("likes")) {
html += '<span class="item item-social' + separator + '">' +
Alfresco.DocumentList.generateLikes(this, record) +
'</span>';
separator = " item-separator";
}
if (!this.myCustomIsSocialDisabled("comments") &&
jsNode.permissions.user.CreateChildren) {
html += '<span class="item item-social' + separator + '">' +
Alfresco.DocumentList.generateComments(this, record) +
'</span>';
separator = " item-separator";
}
if (!this.myCustomIsSocialDisabled("quickShare") && !record.node.isContainer &&
Alfresco.constants.QUICKSHARE_URL) {
html += '<span class="item' + separator + '">' +
Alfresco.DocumentList.generateQuickShare(this, record) +
'</span>';
separator = " item-separator";
}
return html;
},
// Overwrite registerRenderer which was originally defined in:
// webapps/share/components/documentlibrary/documentlist.js:1789
registerRenderer: function DL_registerRenderer(propertyName, renderer) {
if (Alfresco.util.isValueSet(propertyName) &&
Alfresco.util.isValueSet(renderer) &&
!this.myCustomIsRendererDisabled(propertyName)) {
if (propertyName === "social") {
this.renderers[propertyName] = this.myCustomSocialRegisterRenderer;
} else {
this.renderers[propertyName] = renderer;
}
return true;
}
return false;
}
}, true);
Then you can disable the links by updating myCustomDisabledRenderers and/or mySocialDisabledRenderers.
This way also allows you to create a module that disable (for example) the "comments on documents" or "likes on document" feature independently in only 6 easy steps!
Example, how to make a module that only disable comments on documents in 6 steps
Important: first remove the "comment disabling" from the documentlist.js of the main module.
myCustomDisabledSocials: ["favourites", "likes", "quickShare"],
Create a new module "my-custom.nocomment" with the same structure.
<extension>
<modules>
<module>
<id>Main module of my custom extension</id>
[...]
</module>
<module>
<id>No comment module of my custom extension</id>
<version>${project.version}</version>
<customizations>
<customization>
<targetPackageRoot>org.alfresco</targetPackageRoot>
<sourcePackageRoot>my-custom.nocomment</sourcePackageRoot>
</customization>
</customizations>
</module>
</modules>
</extension>
Add the FTL...
src/main/resources/alfresco/site-webscripts/my-custom/nocomment/components/documentlibrary/documentlist-v2.get.html.ftl
<#-- Add a Javascript declaration -->
<#markup id="my-custom-js" target="js" action="after">
<#script type="text/javascript" group="documentlibrary"
src="${url.context}/res/my-custom/nocomment/components/documentlibrary/documentlist.js"/>
</#>
then the Javascript...
src/main/resources/META-INF/my-custom/nocomment/components/documentlibrary/documentlist.js
Alfresco.DocumentList.prototype.myCustomDisableSocial("comment");
and then I'm happy, clap along if you feel like everything's just got smooth!
Notes:
The nocomment module depends on the main module.
It is important for the nocomment module to be loaded after the main module (in http://localhost:8080/share/page/modules/deploy).
In order for the nocomment module to be complete, you also need to disable comments from the document details page, see below.
Disable comments from the document details page
Even if, this one is documented elsewhere, I spent so much time searching around these few days that I feel like I need to be as comprehensive as possible.
src/main/resources/alfresco/site-data/extensions/my-custom-extension.xml
Add this to your my-custom.nocomment module declaration and you will get rid of the comments form and list from the document details page.
[...]
<module>
<id>No comment module of my custom extension</id>
[...]
<components>
<component>
<region-id>comments</region-id>
<source-id>document-details</source-id>
<scope>template</scope>
<sub-components>
<sub-component id="default">
<evaluations>
<evaluation id="guaranteedToHide">
<render>false</render>
</evaluation>
</evaluations>
</sub-component>
</sub-components>
</component>
</components>
</module>
[...]
src/main/resources/alfresco/site-webscripts/my-custom/nocomment/components/node-details/node-header.get.js
This is for disabling the button on the header of the document details page.
// Disable comments
for (var i = 0; i < model.widgets.length; i++) {
if (model.widgets[i].id == "NodeHeader") {
model.widgets[i].options.showComments = false;
}
}
// And since it does not work, disable comments this way too
model.showComments = "false";
Note: I did not test these snippets, they have been taken from my project after "anonymization" (basically renaming the module). Let me know if you find mistakes.
What you are looking for is more than likely generated by client-side JavaScript. You should use share-config-custom.xml to set Share to development mode, like this:
<alfresco-config>
<!-- Put Share Client in debug mode -->
<config replace="true">
<flags>
<client-debug>true</client-debug>
<client-debug-autologging>false</client-debug-autologging>
</flags>
</config>
</alfresco-config>
Then, use firebug or your browser's developer console to step through the client-side JavaScript. You should be able to find the point where the document library elements are rendered.
You can override Alfresco's client-side JavaScript components with your own components. Please put them in your own namespace to avoid collisions with Alfresco's.
I did it by commenting the {social} line in file share-documentlibrary-config.xml in share/src/alfresco/share-document-config
...
<metadata-templates>
<!-- Default (fallback) -->
<template id="default">
<line index="10" id="date">{date}{size}</line>
<line index="20" id="description" view="detailed">{description}</line>
<line index="30" id="tags" view="detailed">{tags}</line>
<line index="40" id="categories" view="detailed" evaluator="evaluator.doclib.metadata.hasCategories">{categories}</line> -->
<!-- <line index="50" id="social" view="detailed">{social}</line> -->
</template>
...
It works!
I'm not sure if I understand well your question - you're trying to hide some columns from particular view in alfresco explorer? If so, you need to edit /jsp/browse/browse.jsp file, but I think that's not a good idea. Maybe implementing your own NodePropertyResolver should be better way, have look at my older blogpost on this topic: http://www.shmoula.cz/adding-columns-to-custom-browse-jsp/
It looks like all of it is in: \opt\alfresco-4.0.d\tomcat\webapps\share\components\documentlibrary\documentlist.js
I think the trick is in this.registerRenderer("social"...) to return html before line 1981 (after favorites before likes) supposing you want to keep at least faorite

Resources