Dynamic list constraint on Alfresco - constraints

I'm trying to follow the examples provided in this post, to create a dynamic list constraint in Alfresco 3.3.
So, I've created my own class extending ListOfValuesConstraint:
public class MyConstraint extends ListOfValuesConstraint {
private static ServiceRegistry registry;
#Override
public void initialize() {
loadData();
}
#Override
public List getAllowedValues() {
//loadData();
return super.getAllowedValues();
}
#Override
public void setAllowedValues(List allowedValues) {
}
protected void loadData() {
List<String> values = new LinkedList<String>();
String query = "+TYPE:\"cm:category\" +#cm\\:description:\"" + tipo + "\"";
StoreRef storeRef = new StoreRef("workspace://SpacesStore");
ResultSet resultSet = registry.getSearchService().query(storeRef, SearchService.LANGUAGE_LUCENE, query);
// ... values.add(data obtained using searchService and nodeService) ...
if (values.isEmpty()) {
values.add("-");
}
super.setAllowedValues(values);
}
}
ServiceRegistry reference is injected by Spring, and it's working fine. If I only call loadData() from initialize(), it executes the Lucene query, gets the data, and the dropdown displays it correctly. Only that it's not dynamic: data doesn't get refreshed unless I restart the Alfresco server.
getAllowedValues() is called each time the UI has to display a property having this constraint. The idea on the referred post is to call loadData() from getAllowedValues() too, so the values will be actually dynamic. But when I do this, I don't get any data. The Lucene query is the same, but returns 0 results, so my dropdown only displays -.
BTW, the query I'm doing is: +TYPE:"cm:category" +#cm\:description:"something here", and it's the same on each case. It works from initialize, but doesn't from getAllowedValues.
Any ideas on why is this happening, or how can I solve it?
Thanks
Edit: we upgraded to Alfresco 3.3.0g Community yesterday, but we're still having the same issues.

This dynamic-list-of-values-constraint is a bad idea and I tell you why:
The Alfresco repository should be in a valid state all the time. Your (dynamic) list of constraints will change (that's why you want it to be dynamic). Adding items would not be a problem, but editing and removing items are. If you would remove an item from your option-list, the nodes in the repository with this property value will be invalid.
You will not be able to fix this easily. The standard UI will fail on invalid-state-nodes. Simply editing this value and setting it to something valid will not work. You have been warned.
Because the default UI widget for a ListConstraint is a dropdown, not every dropdown should be a ListConstraint. ListConstraints are designed for something like a Status property: { Draft, Waiting Approval, Approved }. Not for a list of customer-names.
I have seen this same topic come up again and again over the last few years. What you actually want is let the user choose a value from a dynamic list of options (combo box). This is a UI problem, not a dictionary-model-issue. You should setup something like this with the web-config-context.xml (Alfresco web UI) or in Alfresco Share. The last one is more flexible and I would recommend taking that path.

Related

Adding type to a document uploaded in Alfresco through behaviour

I am trying to associate a document type to xz:xylo, whenever a document is uploaded in a particular workspace of Alfresco, it should get attached to a type which I defined in xylomodel.xml.
I am trying to achieve this via Alfresco behaviour as procceding via Share has some limitation for my requirement.
Can anyone please correct me if the code attached is syntactically correct and I am approaching correctly.
enter code here
public class ApplyXyloAspect implements NodeServicePolicies.OnCreateNodePolicy {`
private NodeService nodeService;
private PolicyComponent policyComponent;
// Behaviours
private Behaviour onCreateNode;
}
/**
^When a document of type #XyloCmsType(name = "X:xz:Xylo") is created than aspects from xyloModel.xml
^needs to be applied
*/
public void init() {
// Create behaviours
if workspace=workspace://SpacesStore/973e1b8d-bf61-8196-3278-fbbf0b4375gg
org.alfresco.repo.node.NodeServicePolicies this.onCreateNode = new JavaBehaviour(this, "onCreateNode", NotificationFrequency.FIRST_EVENT);
// Bind behaviours to node policies
this.policyComponent.bindClassBehaviour(Qname.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
Qname.createQName(XYLO.NAMESPACE_XYLO_CONTENT_MODEL, XYLO.TYPE_xz_xyloModel),
this.onCreateNode
);
}
Depending on your requirements you might be better off achieving this through Folder Rules.
If folder rules are not adequate, or if I'm misunderstanding your use of the very specific NodeRef of workspace://SpacesStore/973e1b8d-bf61-8196-3278-fbbf0b4375gg then I would just check in the onCreateNode method if the created node's parent matches that NodeRef, rather than trying to check in the init method.
so in your init method you would just do something like this:
this.onCreateNode = new JavaBehaviour(this, "onCreateNode", Behaviour.NotificationFrequency.FIRST_EVENT);
this.policyComponent.bindClassBehaviour(NodeServicePolicies.OnCreateNodePolicy.QNAME, Qname.createQName(XYLO.NAMESPACE_XYLO_CONTENT_MODEL, XYLO.TYPE_xz_xyloModel), this.onCreateNode);
And then just check if the node is a child of the node you're trying to have be the parent, in this case you said it would be workspace://SpacesStore/973e1b8d-bf61-8196-3278-fbbf0b4375gg.
So the onCreateNode method would look something like this.
#Override
public void onCreateNode(ChildAssociationRef childAssociationRef){
NodeRef idealParentNodeRef = new NodeRef("workspace://SpacesStore/973e1b8d-bf61-8196-3278-fbbf0b4375gg");
NodeRef nodeRef = childAssociationRef.getChildRef();
NodeRef parentRef = childAssociationRef.getParentRef();
//First double check and make sure all the nodes exist.
if(nodeService.exists(nodeRef) && nodeService.exists(parentRef) && nodeService.exists(idealParentNodeRef)){
//then check if the parentRef and the idealParentNodeRef match
if(parentRef.equals(idealParentNodeRef)){
nodeService.addAspect(nodeRef, /*QName of the Aspect you want to add*/);
}
}
}
If you know for a fact the node/workspace you're uploading to will be very specific every time you could just do this, though I would probably also suggest throwing in some error handling, logging, etc. but this would get you started at least.
Note that, generally, you shouldn't necessarily expect the NodeRef to stay the same every time, granted, I'm just showing you what you could do based on the information from your post rather than what you should do (which would be finding some other way to reference the NodeRef/workspace you're trying to use, and going on from there, depending on whether that NodeRef/workspace is a Folder or Site, or something else).
Hope this helps.

filtering realm list doesn't update list view

I am just picking up Realm and was trying out the QuickJournal example present on GitHub in Xamarin Forms
After the entries are load up in the list view from the constructor as below
public JournalEntriesViewModel()
{
_realm = Realm.GetInstance();
Entries = _realm.All<JournalEntry>();
.. .
}
I added a search bar and was trying to filter and update the Entries collection
public string Filter
{
get
{
retur _filter;
}
set
{
_filter = value;
Filter();
}
}
private void Filter()
{
Entries = _realm.All<JournalEntry>().Where(i => i.Title.StartsWith(_filter));
}
The Filter property is bound to the SearchBar Text in Xaml and the Entries list changes but the UI never updates. My impression was that since Realm uses Fody the notification is propagated to the UI to update.
I have also tried the below change using ToList to fire off the query & map it to realm core as mentioned in the documentation but to no avail. Converting Entries to RealmCollection doesn't work either
Entries = _realm.All<JournalEntry>().ToList().Where(i => i.Title.StartsWith(_filter));
Can someone kindly explain what I am missing here
Many Thanks
I managed to workaround this by firing a PropertyChanged event for the Entries property to update the UI after assigning the value.

How do I redraw UICollectionView when MvxCollectionViewSource's data changes?

I am using Xamarin and MVVMCross to implement an IOS app. A view I'm working on is displaying correctly, but ONLY when I hard-code the data being bound to inside my ViewModel and NOT when (as by design and necessity) my data arrives late from SQLite, only an indeterminate time after the view is shown.
What I am using and accomplished so far:
Working/Showing storyboard for my View that has a UICollectionView
inside (called: CollectionView in code below)
Custom layout and XIB file for every UICollectionViewCell that also displays correctly in my view
A view that works and displays correctly only if ViewModel data is fully populated the moment ViewDidLoad() is called.
Problem:
My data in my ViewModel is updated by the Model's databases in an uncertain amount of time whilst the view is happily being shown. When I bind the data as shown below (and trying two-way/one-way bindings and the like as well), I don't get updates on my view as the final data comes in later.
What I can't seem to do:
Redraw the UICollectionView or maybe refresh the
MvxCollectionViewSource below to ensure that as the ViewModel's data changes, I can actually redraw the UICollectionView and show my
custom cells with new and updated data.
THE CODE(TM)
The CollectionView cells are implemented as follows. I followed all examples online and from that Stuart Bloke and his Kittens to make sure I implement all the patterns exactly the same:
[Register("MyCell")]
public partial class MyCell : MvxCollectionViewCell
{
public static readonly UINib Nib = UINib.FromName("MyCell", NSBundle.MainBundle);
public static readonly NSString Key = new NSString("MyCell");
public MyCell(IntPtr handle) : base(handle)
{
this.DelayBind(() => {
var set = this.CreateBindingSet<MyCell, SomeModelClass>();
set.Bind(Label1).To(item => item.Label1);
set.Bind(Label2).To(item => item.Label2);
set.Apply();
});
}
public static MyCell Create()
{
return (MyCell)Nib.Instantiate(null, null)[0];
}
}
My ViewDidLoad() in the View looks something like this:
CollectionView.RegisterNibForCell(MyCell.Nib, MyCell.Key);
var source = new MvxCollectionViewSource(CollectionView, MyCell.Key);
CollectionView.Source = source;
var set = this.CreateBindingSet<MyView, MyViewModel>();
set.Bind(source).To(vm => vm.ListOfStuff);
set.Apply();
CollectionView.ReloadData();
NB! The ListOfStuff shown above is really just a List of a custom class containing 2 strings right now.
TL:DR: I don't know ListOfStuff's values the moment I call the above code. When I hard-code them in the ViewModel, I get joy. If I don't, I don't, even as data gets updated correctly later.
I now reach out to you, the neurons of the brain of crowdsourcing...
Instead of using a List<T> use ObservableCollection<T> and new items should be added to the CollectionView.
The UI needs to know when the collection has changed. ObservableCollection<T> implements INotifyCollectionChanged and INotifyPropertyChanged and communicates with the UI when the collection changes.
You shouldn't need ReloadData() anymore if you're using ObservableCollection<T>.
This extension method might be of use when adding range of IEnumerable<T>
public static class ObservableCollectionExtensionMethod
{
public static void AddRange<TSource>(this ObservableCollection<TSource> source, IEnumerable<TSource> collection)
{
foreach (var i in collection) source.Add(i);
}
}

Javafx TableView Edit Validation

I build a little JavaFX TableView for displaying data. The user should be able to edit the data in the tableview. The problem is: only specific values are allowed in certain fields. If the user entered a wrong value, the field is set to 0.
Here is my Class:
private ObservableList shots;
#FXML
void initialize() {
this.shots = FXCollections.observableArrayList(match.getShots()); // values from database
tblShots.setItems(shots);
tblShots.setEditable(true);
lblserienid.setText(GUIConstants.idPlaceHolder);
lblresult.setText(GUIConstants.idPlaceHolder);
colShotID.setCellValueFactory(new PropertyValueFactory<Schuss, String>("idSchuss"));
colRing.setCellValueFactory(new PropertyValueFactory<Schuss, String>("ringString"));
colRing.setCellFactory(TextFieldTableCell.forTableColumn());
colRing.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Schuss, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<Schuss, String> t) {
Schuss s = (Schuss) t.getTableView().getItems().get(
t.getTablePosition().getRow());
try {
int ring = Integer.parseInt(t.getNewValue());
s.setRing(ring);
} catch (Exception ex) {
s.setRing(0);
}
SerienauswertungViewController.this.refreshTable();
}
});
colRing.setEditable(true);
// .... omitted
}
private void refreshTable(){
if(shots.size()>0) {
btnDeleteAll.setDisable(false);
btnEdit.setDisable(false);
int res = 0;
for(int i=0;i<shots.size();i++){
Schuss s = (Schuss)shots.get(i);
res += s.getRing();
}
lblresult.setText(""+res);
}
else {
btnDeleteAll.setDisable(true);
btnEdit.setDisable(true);
lblresult.setText(GUIConstants.idPlaceHolder);
}
}
So when I edit a tableviewcell and enter "q" (this value is not allowed) and press enter, the debugger jumps in the above catch block, sets the specific value in the observablelist to 0 (I can see this in the debugger, when I expand this object) but the tableviewcell still displays q instead of 0 (which has been corrected by the system)...
Why does the tableview not show the right values of the observablelist-Object???
This was required but brandnew since Java8u60 (yes - they changed API in an udpate!?!) there is a refresh() method on the TableView itself.
/**
* Calling {#code refresh()} forces the TableView control to recreate and
* repopulate the cells necessary to populate the visual bounds of the control.
* In other words, this forces the TableView to update what it is showing to
* the user. This is useful in cases where the underlying data source has
* changed in a way that is not observed by the TableView itself.
*
* #since JavaFX 8u60
*/
public void refresh() {
getProperties().put(TableViewSkinBase.RECREATE, Boolean.TRUE);
}
It is so new, it´s not even in the official oracle docs... So I cannot provide a link.
cheers.
Okey this seems to be a bug. I used a work around which is mentioned here:
tblShots.getColumns().get(1).setVisible(false);
tblShots.getColumns().get(1).setVisible(true);
Though the refresh() definitely works in conjunction with an upgrade to 8u60, the large project on which I work is currently stuck on 8u51 and cannot reasonably move to u60 any time soon. I tried to implement the code in the refresh() that Rainer referenced in place of the other kluges mentioned above, specifically setting the column invisible/visible. However, simply implementing
getProperties().put(TableViewSkinBase.RECREATE, Boolean.TRUE);
did not work within u51. Upon doing more googling, I came across the JavaFx/Oracle Jira issue here:
https://bugs.openjdk.java.net/browse/JDK-8098085
If you open the rt22599.patch attachment you will find changes to various skins, specifically for TableViews, the TableViewSkinBase. This module is not delivered in the src-javafx.zip that comes with the jdk install. Looking for info on how to possibly incorporate the SkinBase change to a u51 install.
Since JavaFX 8u60 you can use(assuming tableView is an instance of TableView class):
tableView.refresh();
It worked for me

Retrieve names of Online users connected to a Server

I asked this question before which has got a very good response. But as I am new to asp.net (and jquery) cant understand how the program is flowing.
Summary:
I have created a Basic chat application. Now I am trying to add a advanced function like whenever a user is online (connected to a server), the server should show or broadcast the available online user's username to all the users connected to that server.
By referring the responses to the previous question (s0nica and VinayC), I modified my class file and jquery file, which are giving errors as shown in the below links. (I think I am very close)
Chat.cs (Two errors, I mentioned errors in between code comments)
file.js (Working fine, refer it if you need to)
Please have a look to the above files and assist me.
PS: In the previous post, I was thinking that if I change the Global.asax code, my problem will be solved.. which I realized later as wrong..
Your first error from:
Clients.joins(Context.ConnectionId, Caller.username, DateTime.Now);
Shold be:
Clients.All.joins(Context.ConnectionId, Clients.Caller.username, DateTime.Now);
Other errors associated with it: In your JS file it should be:
Line 15
chat.state.username = chatUsername;
Second error:
The error is exactly as it states, you do not have a toList function off of your dictionary object. Secondly you can't plainly convert a List or a string directly to a Chat object.
Based on your setup you currently dont have a proper "user" list to return. Right now you're saving a List to represent an individual user. You might want to try changing your dictionary object to be something like
static ConcurrentDictionary<string, User> _users = new ConcurrentDictionary<string, User>();
Where User is:
public class User
{
public string Name { get; set; }
public string ConnectionID { get; set; }
}
Then on your Joined function you could just do:
public void Joined()
{
User user = new User
{
Name = Clients.Caller.username,
ConnectionID = Context.ConnectionId
};
_users.TryAdd(user.ConnectionID, user);
Clients.All.joins(user.ConnectionID, user.Name, DateTime.Now);
}
Lastly your GetConnectedUsers would end up(make sure you're 'using System.Linq;'):
public List<User> GetConnectedUsers()
{
return _users.Values.ToList();
}
I probably went a little bit overboard but hopefully this helps!
If you need a reference to the change log from SignalR 0.5.3 to 1.0 alpha here's a great post on all of the modifications:
http://weblogs.asp.net/davidfowler/archive/2012/11/11/microsoft-asp-net-signalr.aspx

Resources