Returning ItemStats from Tridion UGC - tridion

I was wondering if anyone can offer any pointers on this one. I'm trying to return ItemStats from the Tridion UGC web service but I'm getting the following error when trying to bind the results:-
The closed type TridionWebUGC.CDS.ItemStat does not have a corresponding LastRatedDate settable property.
An example of code is:-
WebServiceClient ugcCall2 = new WebServiceClient();
Uri uri = new Uri("http://new.ugc.service/odata.svc");
CDS.ContentDeliveryService cds = new CDS.ContentDeliveryService(uri);
var myItemStats = cds.ItemStats.Where(p => p.PublicationId == 68 && p.Id == 17792 && p.Type==16);
I can get comments and ratings with no problem. E.g.
var myComments = cds.Comments.Where(p => p.ItemId == 17805).OrderBy(p => p.CreationDate);
It's just ItemStats that are giving me an issue. Anybody any ideas?
Thanks
John

Unfortunately, the metadata of the UGC WebService is not correct in regards to the ItemsStats. For you it means that the webservice metadata does not expose the fact that the ItemStat entity contains the LastRatedDate property. This makes your .NET proxies not to be aware of this property and makes your query fail.
To work-around this defect you have two option:
Add to your service the following property: cds.IgnoreMissingProperties = true;. Advantage of this approach is that you're done with it in 2 sec. Disadvantage is that you will not be able to access that property (in case you actually use it);
Modify the proxies generated by Visual Studio and manually add that property to the ItemStat class. Advantage of this approach is that you will be able to access the property from your project. Disadvantage is that it's totally not manageable from the coding point of view, you need to be careful when you upgrade or regenerate the proxies and it's easy to make a mistake while manually adding the property.
Note 1: to access the metadata of your webServer from the browser your can go to /odata.svc/$metadata.
Note 2: on a closer look there are 2 properties missing from the webService metadata: LastRatedDate and LastCommentedDate.
Hope this helps.

Related

Adding a button for google signin using f#/fable/asp.net/react

I'm working with the SAFE stack (https://safe-stack.github.io/) and through the example dojo. It's great so far.
I'd like to extend the example to include a button to login/auth via Google. So I looked at an example on the Google website (https://developers.google.com/identity/sign-in/web/build-button). And then I had a look how to do authentication using ASP.NET (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-2.1&tabs=aspnetcore2x) As a result I ended up confused as to how to integrate this into a SAFE project. Can someone tell me what they would do? SHould I be trying to use ASP.NET Identity or should I be using the JWT approach? I don't even know if they are the same since I'm very new to web frameworks.....
The other question I have is how would one inject raw Javascript into the client side of a SAFE project. The google example above shows raw JS/CSS/HTML code? Should I be injecting that as is or should I look in React for some button that does this and map that idea back through Fable?
Setting up OAuth
The easiest way to use Google OAuth is to wait until the next release of Saturn, at which point Saturn will include the use_google_oauth feature that I just added. :-) See the source code if you're interested in how it works, though I'm afraid you can't implement this yourself with use_custom_oauth because you'll run into a type error (the underlying ASP.NET code has a GoogleOptions class, and use_custom_oauth wants an OAuthOptions class, and they aren't compatible).
To use it, add the following to your application CE:
use_google_oauth googleClientId googleClientSecret "/oauth_callback_google" []
The last parameter should be a sequence of string * string pairs that represent keys and values: you could use a list of tuples, or a Map passed through Map.toSeq, or whatever. The keys of that sequence are keys in the JSON structure that Google returns for the "get more details about this person" API call, and the values are the claim types that those keys should be mapped to in ASP.NET's claims system. The default mapping that use_google_oauth already does is:
id → ClaimTypes.NameIdentifier
displayName → ClaimTypes.Name
emails[] (see note) → ClaimTypes.Email
Those three are automatically mapped by ASP.NET. I added a fourth mapping:
avatar.url → `"urn:google:avatar:url"
There's no standard ClaimTypes name for this one, so I picked an arbitrary URN. Caution: this feature hasn't been released yet, and it's possible (though unlikely) that this string might change between now and when the feature is released in the next version of Saturn.
With those four claim types mapped automatically, I found that I didn't need to specify any additional claims, so I left the final parameter to use_google_oauth as an empty list in my demo app. But if you want more (say you want to get the user's preferred language to use in your localization) then just add them to that list, e.g.:
use_google_oauth googleClientId googleClientSecret "/oauth_callback_google" ["language", "urn:google:language"]
And then once someone has logged in, look in the User.Claims seq for a claim of type "urn:google:language".
Note re: the emails[] list in the JSON: I haven't tested this with a Google account that has multiple emails, so I don't know how ASP.NET picks an email to put in the ClaimTypes.Email claim. It might just pick the first email in the list, or it might pick the one with a type of account; I just don't know. Some experimentation might be needed.
Also note that third-party OAuth, including GitHub and Google, has been split into a new Saturn.Extensions.Authorization package. It will be released on NuGet at the same time that Saturn's next version (probably 0.7.0) is released.
Making the button
Once you have the use_google_oauth call in your application, create something like the following:
let googleUserIdForRmunn = "106310971773596475579"
let matchUpUsers : HttpHandler = fun next ctx ->
// A real implementation would match up user identities with something stored in a database, not hardcoded in Users.fs like this example
let isRmunn =
ctx.User.Claims |> Seq.exists (fun claim ->
claim.Issuer = "Google" && claim.Type = ClaimTypes.NameIdentifier && claim.Value = googleUserIdForRmunn)
if isRmunn then
printfn "User rmunn is an admin of this demo app, adding admin role to user claims"
ctx.User.AddIdentity(new ClaimsIdentity([Claim(ClaimTypes.Role, "Admin", ClaimValueTypes.String, "MyApplication")]))
next ctx
let loggedIn = pipeline {
requires_authentication (Giraffe.Auth.challenge "Google")
plug matchUpUsers
}
let isAdmin = pipeline {
plug loggedIn
requires_role "Admin" (RequestErrors.forbidden (text "Must be admin"))
}
And now in your scope (NOTE: "scope" will probably be renamed to "router" in Saturn 0.7.0), do something like this:
let loggedInView = scope {
pipe_through loggedIn
get "/" (htmlView Index.layout)
get "/index.html" (redirectTo false "/")
get "/default.html" (redirectTo false "/")
get "/admin" (isAdmin >=> htmlView AdminPage.layout)
}
And finally, let your main router have a URL that passes things to the loggedInView router:
let browserRouter = scope {
not_found_handler (htmlView NotFound.layout) //Use the default 404 webpage
pipe_through browser //Use the default browser pipeline
forward "" defaultView //Use the default view
forward "/members-only" loggedInView
}
Then your login button can just go to the /members-only route and you'll be fine.
Note that if you want multiple OAuth buttons (Google, GitHub, Facebook, etc) you'll probably need to tweak that a bit, but this answer is long enough already. When you get to the point of wanting multiple OAuth buttons, go ahead and ask another question.

Linq2Dynamodb run expression string on Table

I have an ASP.NET Web API site which records all objects in AWS DynamoDb. I took a quick look at linq2Dynamodb. It seems that the common way to use it is like:
var moviesTable = ctx.GetTable<Movie>();
var inceptionMovie = moviesTable
.Where(m => m.Genre == "Thriller" && m.Title == "Inception")
.Single();
But I want some API like:
moviesTable.Execute(string querystring);
The reason is that from the Web API, I usually get some query like:
http://host/service.svc/Orders?$filter=ShipCountry eq 'France'
I'd like to pass the filter string "ShipCountry eq 'France'" here. Do anyone know if there is a way for me to do this? Thanks.
With Linq2DynamoDb you can expose your DynamoDb tables as OData resources. Please, see the doc here.
As soon as you have an OData resource, you can query it with OData queries of any valid kind.
And you can use any OData client library for that. E.g. with System.Data.Services.Client you would say something like:
var entities = myContext.Execute<MyEntityType>(new Uri("<here goes URI with valid OData query string>"), "GET", true);
or just construct a LINQ query on the client side.
The drawback is that you will need to host your OData service somewhere (DynamoDb itself doesn't support OData, so that's the reason).
The advantages are:
you will be able to cache your data in ElastiCache.
you will be able to implement custom authentication inside your service.
Sorry for a bit late answer.

The given key was not present in the dictionary while retrieving pre-image entity crm 2013

i have registered pre_image with the name 'prentity' and alias 'prentity' and i am retrieving
it as follow
Entity pentity = (Entity)context.PreEntityImages["prentity"];
but giving error 'The given key was not present in the dictionary.'
If you have registered the plugin in create mode the preimage will not be available. May be that is the reason why that image is not available.
Please refer the link below where it explains about the pre and post images.
https://community.dynamics.com/crm/b/crminogic/archive/2010/07/26/pre-image-38-post-image-explained-33.aspx
This is because you don`t register pre-image.
My piece of advice: It is depend on situation, but you can use Target.
Example:
Entity entity = (Entity)context.InputParameters["Target"];
Target contain all fields that user has fill up.
here is the code to get preImage:
var currentRecord= (context.PreEntityImages != null && context.PreEntityImages.Contains("prentity")) ? context.PreEntityImages["prentity"] : null;
NB: you can not use preImage for an "OnCreate" Message,

StructureGroup Details using the Content Delivery/Broker API

I am trying to get all the structure groups published in a given publication using the PublicationID. I am expecting to get the structure groups with StructureGroupCriteria by passing the Root Structure Group TCM ID but getting page ids (I am expecting SGs).
Now I am trying to loop through the list and get details of each structuregroup. I did not find any API (.net) to get these details and also the API is returning only Pages.
What I have done and working so far using StructureGroupCriteria, returns list of Page IDs instead of SG IDs
PublicationCriteria pubCriteria = new PublicationCriteria(pubID);
// Root StructureGroup TCM ID -- tcm:45-3-4
StructureGroupCriteria sgCriteria = new StructureGroupCriteria("tcm:45-3-4", true);
Criteria allSGsInPub = CriteriaFactory.And(pubCriteria, sgCriteria);
Query allSGs = new Query(allSGsInPub);
string[] sgInfo = allSGs.ExecuteQuery();
Response.Write("Total : " + sgInfo.Length);
foreach (string sgid in sgInfo ) {
// HOW DO I get the Structure Group Details here
//TCMURI sgURI = new TCMURI(sgid);
}
Q # 1 : How to get the all the structuregroups and individual structure group details? (May be something simple, I am not able to find right API).
Q # 2 : How can I get all the structuregroups using ItemTypeCriteria sgCriteria = new ItemTypeCriteria(4); // 4 is SG Item Type .
When I tried this option, the query worked successfully but no results returned. Is this the expected behavior and should we always use StructureGroupCriteria instead of ItemTypeCriteria?
The reason for this approach, I want to avoid using the Root StructureGroup ID which is required with the above code. But at the moment, none of the approaches returning StructureGroup information and I always get Page Information.
Tridion Version: 2011 SP1, .net API.
Note: When I publish I am checking the publish SG info checkbox and published successfully. On Broker DB side, I can see the information on the taxnonomy table as well.
I was playing with Odata service and accidentally I found that I can get all my structure group information from Odata web service.
/cd_webservice/odata.svc/StructureGroups?$filter=PublicationId%20eq%2045
Also, the results are returning child structure groups with a depth parameter.
Just to clarify , using Broker API it is not feasible to get the structure groups (my original question). However, the workaround solution is to use OData Service to get the Structure Groups.
I don't think you will get Structure Groups returned by the Query object.
According to the documentation, when you publish Structure Group information the Structure Group hierarchy is published to the Content Delivery side where it is stored as a taxonomy.
Have you tried using the Taxonomy APIs to get the information you need?

Programicatlly visit (all) ASP.Net page(s) in a website?

In the Security model for out ASP.Net website (.Net 3.5) we store the page name:
page.GetType().Name
as the primary key in a database table to be able to lookup if a user has access to a certain page. The first time a page is visited this record is created automatically in the database.
We have exported these database statements to insert scripts, but each time a new page gets created we have to update the scripts, not a huge issue, but I would like to find an automated way to do this.
I created an attribute that I tagged a few pages with and then wrote a small process to get all the objects that have this attribute, through the reflection create an instance and insert the record using the same code to for page records mentioned above:
IEnumerable<Type> viewsecurityPages = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsDefined(typeof(ViewSecurityAttribute),false));
foreach (Type t in viewsecurityPages)
{
object obj = Activator.CreateInstance(t, false);
//clip..(This code just checks if the record already exists in the DB)
if (feature == null)
{
Attribute attb = Attribute.GetCustomAttribute(t, typeof(ViewSecurityAttribute));
if (attb != null)
{
CreateSecurableFeatureForPage((Page)obj, uow, attb.ToString());
}
}
}
The issue is that page.GetType().Name when the page goes through the actual page cycle process is something like this:
search_accounts_aspx
but when I used the activator method above it returns:
Accounts
So the records don't match the in the security table. Is there anyway to programtically "visit" a webpage so that it goes through the actual page lifecycle and I would get back the correct value from the Name parameter?
Any help/reference will be greatly appreciated.
Interesting problem...
Of course there's a (too obvious?) way to programmatically visit the page... use System.Net.HttpWebRequest. Of course, that requires the URI and not just a handle to the object. This is a "how do we get there from here?" problem.
My suggestions would be to simply create another attribute (or use that same one) which stores the identifier you need. Then it will be the same either way you access it, right?
Alternatively... why not just use a 3rd party web spider/crawler to crawl your site and hit all the pages? There are several free options. Or am I missing something?

Resources