2SXC Web API - Create Content Item Metadata for ADAM Asset - 2sxc

I have a content item the has a field with a library of files. I created a separate content item to store the metadata for the files, which includes an integer field called SortOrder that is used for controlling the order of images on the page.
To make the sorting faster at the client's request, I created a page for admins with a jQuery sortable grid of the images that enables drag-and-drop ordering. This works very well by updating the metadata each time an image is dragged.
The problem is that a metadata entry isn't automatically created each time a new image is dragged into the library, so in this case there is no metadata to update. I can manually create the content item, but there doesn't seem to be a way in the API to tag it as belonging to a particular library item.
Hopefully someone can help me find a way using the 2SXC WebAPI or c# code to add metadata to any library item that doesn't exist. I see in the database that the entities are linked in the ToSIC_EAV_Entities using the AssignmentObjectTypeID and KeyString fields, but I would prefer to not make direct database changes. These changes also seem to require clearing the cache and restarting the application pool to be reflected in the UI.

So as of now this is actually a missing detail in the REST API, but the App.Data.Create has an overload for this. check out https://github.com/2sic/eav-server/blob/master/ToSic.Eav.AppEngine/DataSources/App.cs#L26
So it's not perfect but you should be able to continue your work for now.

Based on your pointers, I was able to create a solution using a custom WebAPI:
var contentTypeName = "PhotoDetails";
var userName = Dnn.User.Username;
var fileId = metadata.FileId.ToString();
MetadataFor target = new MetadataFor()
{
TargetType = 10, // hardcoded based on other content item metadata for no until further understood
KeyString = "file:" + fileId // file number comes from dnn's file table
};
var values = new Dictionary<string, object>()
{
{"Title", metadata.Title},
{"SortOrder", (fileId+99)}, // make new images appear at the end by adding 99 to the fileid so they stay in the upload order
{"IsPrimary", false} // image is not a primary photo by default
};
App.Data.Create(contentTypeName, values, userName, target);
Thanks again for the advice!

Related

How to Upgrade a newly added property in all existing nodes?

We are managing an umbraco site which has over 2000 nodes .We have recently added a new property in one of our master document type,since it is a new property the existing nodes doesn't have the property value .We have to update the value of that property in each node before deploying the new changes.It is very difficult to update the values manually since it takes a lot of time.What we are planning is to use a one time upgrade aspx page or user control to accomplish this job. has anyone ever been into this kind of a situation ?how to make a one time upgrade page ? Any help would be appreciated .
I fairly sure that this isn't in the core and that there hasn't been a package created for exactly this purpose. But it's pretty simple to create a macro containing a razor (cshtml) script that updates a single property for all the descendants of a particular node:
Something like (just from memory of the Level 2 course):
var nodes = #Model.DescendantsOrSelf(-1); //-1 is the root node
foreach (var node in nodes)
{
Document doc = new Document(node.Id);
doc.getProperty("yourProperty").Value = "xxx"; // the getProperty looks wrong, I know
doc.Publish(new User(0)); //User 0 is the admin user
umbraco.library.UpdateDocumentCache(doc.Id);
}
This great package http://our.umbraco.org/projects/developer-tools/content-maintenance-dashboard-package does mass updating, but last time I checked it didn't update single fields - but it might do by now.
I would have thought doing a republish of the xml cache should do this if you go to the following url:
/umbraco/dialogs/republish.aspx?xml=true

Dynamically creating field using Drupal

Okay lets say I have a basic demographic content type (Client) that I am saving as a node.
I need to make a key for an external database that we occasionally need to work with.
I want Drupal to take the fragments of three fields being collected in the Client content type and join them into a single field to be saved within the Client content type. Ideally this should be done when I create a new (Client) node.
The new field should contain the following
First 3 letters of firstname
First 3 letters of lastname
and last 4 digits of Social Security Number.
For example
firstname: John
lastname: Doe
SSN: 123-45-6789
A new field should be created, let’s call it fk_database. Using the example above the field >created would be -> johdoe6789
Can this be done via Rules module, or should I approach this in a different way? If I need to do this via a PHP script where exactly should that be added in the Drupal structure?
The easiest way would probably be to use the Computed Field module, it's pretty much built for exactly what you want to do:
Computed Field is a very powerful CCK field module that lets you add a custom "computed fields" to your content types. These computed fields are populated with values that you define via PHP code. You may draw on anything available to Drupal, including other fields, the current user, database tables, you name it.
If you do need to do it in code though, have a look into hook_entity_presave() which would probably be the best place to run your code from.
I personally would build a custom module to handle this and make use of the node API to handle the custom fields if you want to build them on the fly?
Modules could be created either in the modules directory on the 'root' or really they should be in sites > all > modules
You'll have to switch it on in the backend under site building > modules to enable it
You will be able to hook into the API and handle requests via the case statements e.g.
function hook_node_load($node, $types) {
//do something on node load
}
function hook_node_update($node) {
//do something on node update
}
function hook_node_insert($node) {
//do something on node insert
}

how do I use ClassifiedItemsFilterData for folders in Tridion 2011 CoreService?

I'm rewriting a .NET backend application so that it uses the Tridion 2011 CoreService.
There's one part where it will get a folder in Tridion that uses a particular keyword.
In the current setup, this is done by calling the method 'GetListClassifiedItems' on the keyword itself, but how am I suppose to do this using the CoreService?
There is a ClassifiedItemsFilterData available in the CoreService API, but how do I use it?
I've tried this piece of code:
ClassifiedItemsFilterData filter = new ClassifiedItemsFilterData()
{
ItemTypes = new ItemType[] { ItemType.Folder }
};
XElement list = client.GetListXml("tcm:113-363331-1024", filter);
but it will only return the keyword itself, with URI tcm:113-363331-1024, and not the folders that have been classified with it.
If I add the component ItemType to the filter too, I will get all components that have been classified with this keywordk, but still not that folder.
How do I get the folder too?
When I run a similar test, I do get both Folders and Components in my result
var filter = new ClassifiedItemsFilterData();
filter.ItemTypes = new ItemType[] {ItemType.Folder};
var transactions = client.GetListXml("tcm:1-134-1024", filter);
Console.WriteLine(transactions.ToString());
I added a Keyword field to a Metadata Schema and associate that with the Folder. Is that the same way you did it?
When I remove the item types filter from the code above, I get all Components and Folders classified against that Keyword, but I do not get the Keyword itself. These are all exactly how I'd expect a ClassifiedItemsFilterData to work, so we really should focus on what is different in your scenario.

Grails - Removing an item from a hasMany association List on data bind?

Grails offers the ability to automatically create and bind domain objects to a hasMany List, as described in the grails user guide.
So, for example, if my domain object "Author" has a List of many "Book" objects, I could create and bind these using the following markup (from the user guide):
<g:textField name="books[0].title" value="the Stand" />
<g:textField name="books[1].title" value="the Shining" />
<g:textField name="books[2].title" value="Red Madder" />
In this case, if any of the books specified don't already exist, Grails will create them and set their titles appropriately. If there are already books in the specified indices, their titles will be updated and they will be saved. My question is: is there some easy way to tell Grails to remove one of those books from the 'books' association on data bind?
The most obvious way to do this would be to omit the form element that corresponds to the domain instance you want to delete; unfortunately, this does not work, as per the user guide:
Then Grails will automatically create
a new instance for you at the defined
position. If you "skipped" a few
elements in the middle ... Then Grails
will automatically create instances in
between.
I realize that a specific solution could be engineered as part of a command object, or as part of a particular controller- however, the need for this functionality appears repeatedly throughout my application, across multiple domain objects and for associations of many different types of objects. A general solution, therefore, would be ideal. Does anyone know if there is something like this included in Grails?
removeFrom*
Opposite of the addTo method in that it removes instances from an association.
Examples
def author = Author.findByName("Stephen King")
def book = author.books.find { it.title = 'The Stand' }
author.removeFromBooks(book)
Just ran into this issue myself. It's easy to solve. Grails uses java.util.Set to represent lists. You can just use the clear() method to wipe the data, and then add in the ones you want.
//clear all documents
bidRequest.documents.clear()
//add the selected ones back
params.documentId.each() {
def Document document = Document.get(it)
bidRequest.documents.add(document)
log.debug("in associateDocuments: added " + document)
};
//try to save the changes
if (!bidRequest.save(flush: true)) {
return error()
} else {
flash.message = "Successfully associated documents"
}
I bet you can do the same thing by using the "remove()" method in the case that you don't want to "clear()" all the data.
For a good explanation of deleting a collection of child objects with GORM have a look at the Deleting Children section of this blog post - GORM gotchas part 2
It's recommended reading, as are parts 1 and 3 of the series.
I am just starting to learn Grails myself and saw your question as an interesting research exercise for me. I do not think you can use the conventional data binding mechanism - as it fills in the blanks using some kind of Lazy map behind the scenes. So for you to achieve your goal your "save" method (or is it a function?) is unlikely to contain anything like:
def Book = new Book(params)
You need a mechanism to modify your controller's "save" method.
After some research, I understand you can modify your scaffolding template which is responsible for generating your controller code or runtime methods. You can get a copy of all the templates used by Grails by running "grails install-templates" and the template file you would need to modify is called "Controller.groovy".
So in theory, you could modify the "save" method for your whole application this way.
Great! You would think that all you need to do now is modify your save method in the template so that it iterates through the object entries (e.g. books) in the params map, saving and deleting as you go.
However, I think your required solution could still be quite problematic to achieve. My instinct tells me that there are many reasons why the mechanism you suggest is a bad idea.
For one reason, off the top of my head, imagine you had a paginated list of books. Could that mean your "save" could delete the entire database table except the currently visible page? Okay, let us say you manage to work out how many items are displayed on each page, what if the list was sorted so it was no longer in numerical order - what do you delete now?
Maybe multiple submit buttons in your form would be a better approach (e.g. save changes, add, delete). I have not tried this kind of thing in Grails but understand actionSubmit should help you achieve multiple submit buttons. I certainly used to do this kind of thing in Struts!
HTH
I'm just running into this same issue.
My application's domain is quite simple: it has Stub objects which have a hasMany relationship with Header objects. Since the Header objects have no life of their own, they're entirely managed by the Stub controller and views.
The domain class definitions:
class Stub {
List headers = new ArrayList();
static hasMany = [headers:Header]
static mapping = {headers lazy: false}
}
class Header {
String value
static belongsTo = Stub
}
I've tried the "clear and bind" method but the end result is that the "cleared" objects are left over in the database and grails will just create new instances for the ones that were not removed from the relationship. It does seem to work from an user's perspective, but it will leave lots of garbage objects in the database.
The code in the controller's update() method is:
stubInstance.headers.clear()
stubInstance.properties = params
An example: while editing the -many side of this relationship I have (for a given Stub with id=1):
<g:textField name="headers[0].value" value="zero" id=1 />
<g:textField name="headers[1].value" value="one" id=2 />
<g:textField name="headers[2].value" value="two" id=3 />
in the database there are 3 Header instances:
id=1;value="zero"
id=2;value="one"
id=3;value"two"
after removing header "one" and saving the Stub object the database will have headers:
id=1;value="zero"
id=2;value="one"
id=3;value"two"
id=4;value="zero"
id=5;value="two"
and the Stub object will now have an association with Headers with id=4 and id=5...
Furthermore, without the clearing of the list, if an index is not present in the submitted request.headers list, on data binding grails will keep the existing object at that location unchanged.
The solution that occurs to me is to bind the data, then check the Stub's headers for elements that are not present in the submitted list and remove them.
This looks like a pretty simple scenario, isn't there any built-in functionality to address it?
It's a bit overkill to have to write your own synchronization logic for maintaining relationships, especially when the quirks that make it non-trivial are caused by grails itself.
What about deletion, shouldn't the clear()'ed elements be gone from the database? Am I missing something in the relationship or domain object definitions?
class Stub {
List headers = new ArrayList();
static hasMany = [headers:Header]
static mapping = {
headers lazy: false
**headers cascade: "all-delete-orphan"**
}
}
class Header {
String value
static belongsTo = Stub
}
I have added the cascade property on the owning side of relationship and Now if you try to save the stub, it will take care of removing deleted items from the collection and delete them from the DataBase.

How to dynamically load and switch the resource file in the web app (ASP.NET) without recompiling?

I would like to store the resource files (containing texts for labels etc.) for my web application in the database to be able to edit and create them dynamically later (probably in the UI). My idea was to store the whole resx file in an xml column and simply load it on demand - depending on the language and some other attributes of the current user when he is logging into the application or switching the context. The important thing is that the resources do not only depend on the culture info of the user but also on some context information that can be switched by user without logging off and on again (in our case called "group", not having anything to do with a group of users).
Is it possible somehow to load the contents of the resources from an external source and simply switch them without web application being recompiled by the server ? I know that there are some interfaces on which I could hook up and implement a custom resources provider which reads from the database directly but if it could work somehow with the resx files it would probably make things a lot easier..
Pretty late but since there is no answer as of yet.
System.Resources.ResourceReader resourceReader
= new System.Resources.ResourceReader("PathToResourceFile");
That's pretty much it. Now you can create resource files like en.resx or de.resx and load them depending on the users language. Something like
System.Resources.ResourceReader resourceReader
= new System.Resources.ResourceReader(HttpContext.Current.Request.UserLanguages[0]
+ ".resource");
Keep in mind to provide a default language (resource file) for a user with a language you don't support.
Edit:
Take a look at this link.
The question is 6 years old, but I'm still gonna answer it :)
To read .resx files, you need to use System.Resources.ResXResourceReader class from System.Windows.Forms.dll
This is nicely explained here. Just a quick sample for completeness:
using (ResXResourceReader resxReader = new ResXResourceReader(#".\CarResources.resx"))
{
foreach (DictionaryEntry entry in resxReader) {
// ...
}
}
Sure you can do this easily, and it works for straight XML files. You don't need to use a resx file.
/// <summary>
/// Sets or replaces the ResourceDictionary by dynamically loading
/// a Localization ResourceDictionary from the file path passed in.
/// </summary>
/// <param name="resourceDictionaryFile">The resource dictionary to use to set/replace
/// the ResourceDictionary.</param>
private void SetCultureResourceDictionary(String resourceDictionaryFile)
{
// Scan all resource dictionaries and remove, if it is string resource distionary
for ( int index= 0; index < Resources.MergedDictionaries.Count; ++index)
{
// Look for an indicator in the resource file that indicates the resource is
// swappable. For instance in our files the header contains this:
// <sys:String x:Key="ResourceDictionaryName">Resources-en-CA</sys:String>
if (Resources.MergedDictionaries[index].Contains("ResourceDictionaryName"))
{
if ( File.Exists(resourceDictionaryFile) )
{
Resources.MergedDictionaries.Remove(Resources.MergedDictionaries[index]);
}
}
}
// read required resource file to resource dictionary and add to MergedDictionaries collection
ResourceDictionary newResourceDictionary = new ResourceDictionary();
newResourceDictionary.Source = new Uri(resourceDictionaryFile);
Resources.MergedDictionaries.Add(newResourceDictionary);
}

Resources