I have a custom content model I created for Alfresco that has type with a d:date property. I am able to build the repository and share projects with seemingly no errors. However, I am unable to search by the properties using the data type d:date or d:int. I resolved the d:int problem by changing the data type to d:text and adding a regex constraint, but I'm not sure if that would be prudent for the d:date property.
Is there some additional configuration that I need to supply or create in order to search by properties that are not d:text?
Here is a snippet showing the type declaration:
<types>
<!-- Enterprise-wide generic document type -->
<type name="gl:x">
<title>Document</title>
<parent>cm:content</parent>
<properties>
<property name="gl:period">
<type>d:text</type>
</property>
<property name="gl:year">
<type>d:text</type>
<constraints>
<constraint ref="gl:documentYears" />
</constraints>
</property>
<property name="gl:docType">
<type>d:text</type>
<constraints>
<constraint ref="gl:documentTypeList" />
</constraints>
</property>
<property name="gl:date">
<type>d:date</type>
</property>
</properties>
</type>
</types>
The share search forms and properties forms seem to be rendering correctly, so I don't think that there is any problem within those.
The advanced search page accepts two types of parameters.
One is simply the "keywords" field. This performs a full text search, i.e. it looks for the provided keywords in ANY text property. There is no need to configure the full text search for custom types (e.g. your gl:x) - it automatically picks up any text property in any model in the system.
The other is the group of single parameters: name, title, description, mime-type, modified-date, modifier. These properties can be of any type. A d:date property would be perfectly acceptable here, as the modified-date parameter testifies.
But here custom properties are not picked-up automatically. They need to be configured explicitly.
Notice that in the upper part of the advanced search page is a drop-down called "Look for" with two options: content and folders. The best approach would be to add an option for your content type gl:x and to configure a search form for it.
You can find the definition of the two standard search forms in tomcat/webapps/share/WEB-INF/classes/alfresco/share-form-config.xml. The file is rather long so here are the two sections to look for:
<config evaluator="model-type" condition="cm:content">
<forms>
<!-- Default Create Content form -->
<form>
</form>
<!-- Document Library Create Google Doc form -->
<form id="doclib-create-googledoc">
</form>
<!-- Search form -->
<form id="search">
</form>
</forms>
</config>
<!-- cm:folder type (creating nodes) -->
<config evaluator="model-type" condition="cm:folder">
<forms>
<!-- Document Library Common form -->
<form id="doclib-common">
</form>
<!-- Search form -->
<form id="search">
</form>
</forms>
</config>
I've skipped the details, but what is important is that "cm:content" and "cm:folder" each defines a <form id="search"> with the desired search properties/parameters.
As an experiment you could modify share-form-config.xml directly and add your own definition:
<config evaluator="model-type" condition="gl:x">
<forms>
<!-- Search form -->
<form id="search">
<field-visibility>
<show id="gl:date" />
</field-visibility>
<appearance>
<field id="gl:date">
<control template="/org/alfresco/components/form/controls/daterange.ftl" />
</field>
</appearance>
</form>
</forms>
</config>
Also you have to add the new search form to the AdvancedSearch configuration found in tomcat/webapps/share/WEB-INF/classes/alfresco/share-config.xml:
<config evaluator="string-compare" condition="AdvancedSearch">
<advanced-search>
<forms>
<form labelId="search.form.label.cm_content" descriptionId="search.form.desc.cm_content">cm:content</form>
<form labelId="search.form.label.cm_folder" descriptionId="search.form.desc.cm_folder">cm:folder</form>
<form labelId="search.form.label.gl_x" descriptionId="search.form.desc.gl_x">gl:x</form>
</forms>
</advanced-search>
</config>
Remember to restart alfresco after every change.
When you're satisfied with the results, it would be better to move your custom definitions to a separate share-config-custom.xml in your project (share-config.xml and share-form-config.xml should never be modified directly).
For more details: https://wiki.alfresco.com/wiki/Share_Advanced_Search
Related
I want to customize Teamcity notification email with freemarker template.
It's feasible to get Teamcity data model from freemarker.
The data model is look like below
<#-- #ftlvariable name="project" type="jetbrains.buildServer.serverSide.SProject" -->
<#-- #ftlvariable name="buildType" type="jetbrains.buildServer.serverSide.SBuildType" -->
<#-- #ftlvariable name="build" type="jetbrains.buildServer.serverSide.SBuild" -->
<#-- #ftlvariable name="agentName" type="java.lang.String" -->
<#-- #ftlvariable name="buildServer" type="jetbrains.buildServer.serverSide.SBuildServer" -->
<#-- #ftlvariable name="webLinks" type="jetbrains.buildServer.serverSide.WebLinks" -->
<#-- #ftlvariable name="var.buildFailedTestsErrors" type="java.lang.String" -->
<#-- #ftlvariable name="var.buildShortStatusDescription" type="java.lang.String" -->
<#-- #ftlvariable name="var.buildChanges" type="java.lang.String" -->
<#-- #ftlvariable name="var.buildCompilationErrors" type="java.lang.String" -->
<#-- #ftlvariable name="link.editNotificationsLink" type="java.lang.String" -->
<#-- #ftlvariable name="link.buildResultsLink" type="java.lang.String" -->
<#-- #ftlvariable name="link.buildChangesLink" type="java.lang.String" -->
<#-- #ftlvariable name="responsibility" type="jetbrains.buildServer.responsibility.ResponsibilityEntry" -->
<#-- #ftlvariable name="oldResponsibility" type="jetbrains.buildServer.responsibility.ResponsibilityEntry" -->
I know it's possible to get bean property by its name directly, for example, there is inherited getName() method in jetbrains.buildServer.serverSide.SBuildType, so I can get the name property by buildType.name from freemarker template.
However, for method below, I have no idea how to get buildArtifacts with different parameters from template.
The method doc is as below:
getArtifacts
#NotNull
BuildArtifacts getArtifacts(#NotNull
BuildArtifactsViewMode mode)
Returns accessor for build artifacts. This accessor checks all necessary permissions for accessing files.
Parameters:
mode - view mode to see artifacts
Returns:
build artifacts viewer
The BuildArtifactsViewMode's definition is as below,
Enum BuildArtifactsViewMode
VIEW_ALL
Will show all build artifacts i.e.
VIEW_ALL_WITH_ARCHIVES_CONTENT
Will show all build artifacts and archives content
VIEW_DEFAULT
Will show all user-published artifacts
VIEW_DEFAULT_WITH_ARCHIVES_CONTENT
Will show all user-published artifacts and archives content
VIEW_HIDDEN_ONLY
Will show only hidden build artifacts
Thanks for your help in advance.
I don't know anything about Teamcity, but as a template author, you are reliant on what was exposed to you, and classes and enums aren't exposed in a minimal FreeMarker setup. If you have the power to configurte FreeMarker in Teamcity, or to add Java code that populates the data-model, then you can do something like this (and here I use the Configuration-based approach, but you could add these to the data-model similarly as well):
cfg.setSharedVariable(
"enums",
((BeansWrapper) cfg.getObjectWrapper()).getEnumModels());
and then in the template:
${someMethod(enums['com.example.MyEnum'].FOO)}
and/or you could do this:
cfg.setSharedVariable(
"MyEnum",
((BeansWrapper) cfg.getObjectWrapper()).getEnumModels().get(MyEnum.class.getName()));
and then in the template:
${someMethod(MyEnum.FOO)}
If Teamcity doesn't allow you to do such things, then it's on them to ensure some similar facility to you, and let's hope someone else knows about that...
The problem
There is a content model with conractType property, and data list with conractType column. It is needed to point contextModel.conractType to dataList.conractType. E.g. before insert a property value it should be checked that this valu is present in data list. Also use should select property value from drop down list which corresponds to data list values.
My solution
When try to link model property with data list type directly:
<!-- DataLists-->
<type name="sc:contractType">
<title>Options</title>
<parent>dl:dataListItem</parent>
<properties>
<property name="sc:type">
<title>Type</title>
<type>d:text</type>
</property>
</properties>
</type>
<!-- workflow model-->
<type name="sc:startProcesstask">
<parent>bpm:startTask</parent>
<properties>
<property name="sc:helloName">
<type>d:text</type>
<mandatory>true</mandatory>
<multiple>false</multiple>
</property>
<!-- Error after adding this property -->
<property name="sc:requestCategory">
<type>sc:contractType</type>
<mandatory>true</mandatory>
<multiple>false</multiple>
</property>
</properties>
</type>
I got an error:
Caused by: org.alfresco.service.cmr.dictionary.DictionaryException: 09180002 Property type 'sc:contractType' of property 'sc:requestCategory' is not found
So it seems that I need to create:
Custom validator which check input value
Custom ui element which retrieve all possible list values from contractType column.
Question 1
In this case how to link validator and ui element properly? E.g. data list has type and UUID. Link to UUID is hardcode, but link to type lead to unexpected situation when there is more then one list with values. May be it is needed to have additional binding between list data type and model?
Question 2
I think this problem is common, but it is extremelly dificult to find any piece of code. (A lot of code with separate contextn model and data lists, but no together) Does alfresco provide a build-in solution for link content model property value to data list?
Alfresco's Dictionary has defined several data types that can be used while defining properties in content model Properties
So it wont accept the type that you have defined.
In order to achieve your requirement, you can go for defining sc:requestCategory as child association of sc:startProcesstask
your modified model will looks like:
<!-- DataLists-->
<type name="sc:contractType">
<title>Options</title>
<parent>dl:dataListItem</parent>
<properties>
<property name="sc:type">
<title>Type</title>
<type>d:text</type>
</property>
</properties>
</type>
<!-- workflow model-->
<type name="sc:startProcesstask">
<parent>bpm:startTask</parent>
<properties>
<property name="sc:helloName">
<type>d:text</type>
<mandatory>true</mandatory>
<multiple>false</multiple>
</property>
</properties>
<associations>
<child-association name="sc:requestCategory"">
<target>
<class>sc:contractType</class>
<mandatory>true</mandatory>
<many>false</many>
</target>
</child-association>
</associations>
</type>
I'm trying to extract the URL's of a website that doesn't have a sitemap. I'm using the Web Harvest tool
I have no idea about Java or coding. Could someone please help me out with using this tool.
I want it to run on a specific website (e.g. example.com) and extract every single URL from that website.
Example.com is not a very good example, as it has only one link! :)
Here's my code with some annotations:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<!-- 1: provide inputs -->
<script><![CDATA[
url="http://stackoverflow.com/questions/17635763/trying-to-extract-urls-from-a-website-using-web-harvest";
output_path = "C:/webharvest/";
file_name = "urllist.txt";
output_file = output_path + file_name;
]]></script>
<!-- 5 : save the resulting list in a variable -->
<var-def name="urls">
<!-- 4 : select only links (outputs a list variable) -->
<xpath expression='//a/#href'>
<!-- 3 : convert it to XML, for querying -->
<html-to-xml>
<!-- 2 : load the page -->
<http url="${url}"/>
</html-to-xml>
</xpath>
</var-def>
<!-- 7: write to output file -->
<file action="write" path="${output_file}">
<!-- 6 : convert the list variable into a string with each link on a new line -->
<text delimiter="${sys.cr}${sys.lf}">
<var name="urls" />
</text>
</file>
</config>
You should go through Web harvest user manual at http://web-harvest.sourceforge.net/manual.php which has multiple number of examples.
IzPack TargetPanel lets one select one target directory. However I need to allow users to choose two (one for apps, one for data). How to do that?
You can create a UserInputPanel and get the path as a variable from the user. Then you can use variable substitution anywhere you want. You'll have to add a userInputSpec.xml file and define your own panels (as many as you want). To get a directory, use <field type="dir" ... >
Example userInputSpec.xml from an application of mine. I include mongoDB with the installer and use this to get some settings.
<userInput>
<panel order="0">
<createForPack name="MongoDB" />
<!-- Other settings like port, ip, username, password-->
<field type="staticText" align="left" txt="Select the catalogue where data will be stored." id="staticText.registry.db.data.text" />
<field type="dir" align="left" variable="mongo.data.dir">
<spec txt="Data directory" size="25" set="$INSTALL_PATH\data" mustExist="false" create="true" />
</field>
</panel>
<panel order="1">
<!-- definition of a second panel -->
</panel>
</userInput>
You also need to include the userInputSpec.xml as a resource in your main installation file and add a UserInputPanel element for each panel that you define in userInputSpec.xml
Like this (in the <installation> element:
<resources>
<!-- other resources -->
<res id="userInputSpec.xml" src="userInputSpec.xml" />
</resources>
<panels>
<panel classname="HelloPanel"/>
<panel classname="InfoPanel"/>
<panel classname="LicencePanel"/>
<panel classname="TargetPanel"/>
<panel classname="TreePacksPanel"/>
<panel classname="UserInputPanel"/>
<panel classname="UserInputPanel"/>
<panel classname="InstallPanel"/>
<panel classname="ShortcutPanel"/>
<panel classname="FinishPanel"/>
</panels>
notice the double occurence of
I have two panels defined in my userInputSpec
Make sure that your UserInputPanels appear before InstallPanel because you have to get the variables from the user before copying your files.
This is just an example from my app. See the official documentation to get the idea of what the elements and attributes I used mean. There are many features connected with user input.
I'm using Tapestry 4.
I have several TextFields whose values get passed into Strings in the page class, and they work great as long as there is some content in the fields. Most of them are optional, so I believe I can use the StringTranslator with empty= in that case, but for a couple of fields for which a value is required, I'm having a hard time getting validation to work.
I expected a simple required validator to work:
<component id="myRequiredField" type="TextField">
<binding name="value" value="ognl:stringValue" />
<binding name="validators" value="validators:required" />
</component>
Failing that, I expected minLength to work:
<component id="myRequiredField" type="TextField">
<binding name="value" value="ognl:stringValue" />
<binding name="validators" value="validators:required,minLength=1" />
</component>
Both attempts at validation allow the value as retrieved with getStringValue() to be null upon form submission. My Form and Submit components look like:
<component id="myUpdateForm" type="Form">
<binding name="delegate" value="beans.validationDelegate" />
</component>
<component id="submitUpdate" type="Submit">
<binding name="action" value="listener:doUpdate" />
</component>
It turns out that the validation was working, but I wasn't checking whether my validation delegate had errors before operating on the incoming data. The following seems to be the correct approach to take in any listener that depends on validation, given the setup as listed in the question:
#Bean
public abstract ValidationDelegate getValidationDelegate();
public void doUpdate() {
if (!getValidationDelegate().getHasErrors()) {
// business logic
}
}