IBM ICN 3.0.3 - Ugly display of a ChoiceList for a mono-valued Property in the Content dialog - plugins

Given a Document class, a mono-valued Property of the Entry Template is associated with a ChoiceList. This works well if the ChoiceList has no "sublevels" (Choice).
When a Group Choice is added and the user tries to fill the property, the dialog becomes ugly, as well as displayed below:
Is there a way to automatically unfold the tree view for the root Choices, and moreover to remove the "none" label ("Aucun" in french) as well as the symbolic name of the ChoiceList (blurred here)?
Do I have to write a Plugin to fix the issue?
Update. The purpose of "Aucun" here is to empty the field.

I contacted the support team, and in a few words, it's not possible "out of the box". But I found a workaround.
I wrote a ResponseFilter which catches the response of the request /p8/openContentClass. Turns out its response contains the ChoiceList values:
{
"classes": [{
"parentClassId": "<PARENTCLASSID>",
"template_name": "<ENTRYTEMPLATE>",
/* [...] */
}
],
/* [...] */
"criterias": [/* [...] */, {
"settability": "readWrite",
"defaultOperator": "EQUAL",
"minValue": null,
"uniqueValues": true,
"orderable": false,
"choiceList": {
"choices": /* <----- here */,
"displayName": "CL_ToFilter"
},
/* [...] */
"name": "<propertyName>"
}
]
}
Reformatting "choices" entry to get a one-level Choice List ensure a display on one level. Below the relevant code of the ResponseFilter:
public class ChoiceListValuesResponseFilter extends PluginResponseFilter {
public String[] getFilteredServices() {
return new String[] { "/p8/openContentClass"/* "/p8/openItem"*/ };
}
public void filter(String serverType, PluginServiceCallbacks callbacks,
HttpServletRequest request, JSONObject jsonResponse) throws Exception {
// [...]
JSONArray jsonProperties =
(JSONArray) jsonResponse.get("criterias");
Iterator it = jsonProperties.iterator();
while (it.hasNext()) {
JSONObject jo = (JSONObject) it.next();
if ("<PROPERTYWITHFILTEREDCL>".equals(jo.get("name"))) {
JSONObject choiceListJo = (JSONObject) jo.get("choiceList");
// do the processing here
break;
}
}
}
// [...]
}

Related

In Swagger UI, how can I remove the padlock icon from "anonymous" methods?

I'm creating an API with .Net Core 2.1 and using JSON Web Token (JWT) for authentication.
I have 2 controllers: AuthenticationController and UserController.
I have decorated AuthenticationController with [AllowAnonymous] and UserController with [Authorize].
Swagger is working correctly: it allows me to hit the endpoints in AuthenticationController (SignUp/SignIn) without requesting authorization, and it does request JWT to hit the endpoints in UserController.
However, in Swagger UI, every endpoint of every controller shows a padlock icon as if all of them required authorization. Everything works correctly and as expected but it just bothers me that the endpoints that don't require authorization still show that padlock icon.
Is there a way to remove the padlock icon from those endpoints?
I believe that something can be done with the OperationFilter but I couldn't find a way.
Absolutly, you need to use an IOperationFilter to remove the padlock icon for the anonymous endpoints.
// AuthResponsesOperationFilter.cs
public class AuthResponsesOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
if (authAttributes.Any())
{
var securityRequirement = new OpenApiSecurityRequirement()
{
{
// Put here you own security scheme, this one is an example
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
};
operation.Security = new List<OpenApiSecurityRequirement> { securityRequirement };
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
}
}
}
// Startup.cs
services.AddSwaggerGen(c =>
{
...
c.OperationFilter<AuthResponsesOperationFilter>();
};
Do not forget to remove any call to AddSecurityRequirement in your Startup.cs, otherwise the padlock icon would still be added to all endpoints.
this solution works for SwashBuckle 5.0.0-rc5 and .Net Core 3.1.1 Web API.
You need to :
implement an IOperationFilter interface,
add c.OperationFilter(); in your Startup.cs file
finally remove any call of AddSecurityRequirement
public class AuthResponsesOperationFilter: IOperationFilter {
public void Apply(OpenApiOperation operation, OperationFilterContext context) {
if (!context.MethodInfo.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) &&
!context.MethodInfo.DeclaringType.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute)) {
operation.Security = new List < OpenApiSecurityRequirement > {
new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "bearer"
}
}, new string[] {}
}
}
};
}
}
}
Install Package
Swashbuckle.AspNetCore.Filters
And then when you document your swagger you need to add the below line
options.OperationFilter<SecurityRequirementsOperationFilter >();
Here's an example from .NET 6
builder.Services.AddSwaggerGen(options => {
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "API",
Version = "v1",
Description = "API using .NET 6"
});
options.OperationFilter<SecurityRequirementsOperationFilter>();
});
In startup.cs -> services.AddSwaggerGen , you need to add c.OperationFilter<ApplyOAuth2Security>(); and add below method in stratup.cs which will enable lock/authorize icon in Swagger UI for the action methods which are marked as Authorize only.
private class ApplyOAuth2Security : IOperationFilter
{
/// <inheritdoc/>
public void Apply(Operation operation, OperationFilterContext context)
{
var filterDescriptor = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterDescriptor.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
var authorizationRequired = context.MethodInfo.CustomAttributes.Any(a => a.AttributeType.Name == "AuthorizeAttribute");
if (isAuthorized && authorizationRequired)
{
operation.Security = new List<IDictionary<string, IEnumerable<string>>>
{
new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", new string[] { "openid" } },
},
};
}
}
}

How to Make a app acording to language change

I developed an app that can support two languages. Going beyond that if I need this app for support with many languages. let assume ten languages how to accomplish this requirement
Now what I do is Hard cording it with both languages I used and then retrieve it. If I going to do that for ten languages it does not sound good is there any smart way to do this.
as an example;
I doing with MVVM pattern In my View Model. I use the property and do this its simple way of doing
public string GetPageTitle => AppResources.VitalSignsViewPage_Title;
and my resource file I have two data set if the language has changed it will use write dataset this is what I am doing right now is there any proper smart way to this to many languages.
There is some solutions to achive this. I recommend to you basic
solution ,
Here is an interface for Language Service (It is optionally , if you
using Dependency Injection):
public interface ILanguageService
{
string GetString(string text);
void ChangeLanguage(bool isALang);
bool IsALanguage();
}
You can create a service for localization :
namespace Service.Language
{
public sealed class LanguageService : ILanguageService
{
List<LanguageRow> LanguageList;
private bool IsFirstLang;
public LanguageService()
{
LanguageList = JsonHelper.ReadJSON<List<LanguageRow>>("Service.Language.MultiLanguage.json", typeof(LanguageService));
IsFirstLang = true;
}
public void ChangeLanguage(bool IsFirstLang)
{
IsFirstLang = !IsFirstLang;
}
public bool IsALangueage()
{
return IsFirstLang;
}
public string GetString(string text)
{
string result;
try
{
var row = LanguageList.FirstOrDefault(i => i.Code.Equals(text));
result = IsFirstLang? row.Values[0] : row.Values[1];
}
catch
{
result = text;
}
return result;
}
}
}
Here is a model to serialization for json :
public class LanguageRow
{
public LanguageRow()
{
Values = new List<string>();
}
public string Code { get; set; }
public List<string> Values { get; set; }
}
At last, here is json file : (EN-FR)
[
{
"Code": "VitalSignsViewPage_Title",
"Values": [ "Page Title", "Titre de la page" ]
},
{
"Code": "VitalSignsViewPage_SubTitle",
"Values": [ "Sub Title", "Sous-titre" ]
},
{
"Code": "VitalSignsViewPage_SubSubTitle",
"Values": [ "Sub Sub Title", "Sous sous-titre" ]
}
]
You can access translations like :
ILanguageService _langService = new LangService()
_langService.GetString(AppResources.VitalSignsViewPage_Title);

Actor Method Start/Stop log | Add additional Information

For azure actor services, Actor Method Start Stop logs in Diagnostics window, which looks like below.
How can i add some additional detail such as a Correlation Id every time a method is called ?
{
"Timestamp": "2016-09-14T19:46:40.9955448+05:30",
"ProviderName": "Microsoft-ServiceFabric-Actors",
"Id": 7,
"Message": "Actor method is being invoked. Method name: IStore.GetStoreById, actor type: Backend.Actor.Store.Store, actor ID: STORE_6.",
"ProcessId": 30736,
"Level": "Verbose",
"Keywords": "0x0000F00000000002",
"EventName": "ActorMethod/Start",
"Payload": {
"methodName": "IStore.GetStoreById",
"methodSignature": "System.Threading.Tasks.Task`1[Backend.Models.Store.StoreView] GetStoreById(System.String)",
"actorType": "Backend.Actor.Store.Store",
"actorId": "STORE_6",
"actorIdKind": 2,
"replicaOrInstanceId": 131183360004211655,
"partitionId": "8af1c125-3666-40d0-b630-e3570c41833b",
"serviceName": "fabric:/MultiBannerBackend/StoreActorService",
"applicationName": "fabric:/MultiBannerBackend",
"serviceTypeName": "StoreActorServiceType",
"applicationTypeName": "MultiBannerBackendType",
"nodeName": "_Node_4"
}
}
In order to log custom data for every Actor operation, you can use these methods:
protected override Task OnPreActorMethodAsync(ActorMethodContext c)
protected override Task OnPostActorMethodAsync(ActorMethodContext c)
In order to get the call context, I've found that CallContext.LogicalGetData doesn't work in this situation. The Actor itself fortunately does know about its context. You can get it using some reflection.
For example:
protected override Task OnPreActorMethodAsync(ActorMethodContext c)
{
var correlationID = this.GetActorContext() ?? Guid.Empty.ToString("N");
string message = $"Actor method is being invoked. Method name: {c.MethodName}, actor type: {GetType().FullName}, actor ID: {Id}, CorrelationID:{correlationID}";
ActorEventSource.Current.ActorMessage(this, message);
return Task.FromResult(true);
}
protected override Task OnPostActorMethodAsync(ActorMethodContext c)
{
var correlationID = this.GetActorContext() ?? Guid.Empty.ToString("N");
string message = $"Actor method has completed. Method name: {c.MethodName}, actor type: {GetType().FullName}, actor ID: {Id}, CorrelationID:{correlationID}";
ActorEventSource.Current.ActorMessage(this, message);
return Task.FromResult(true);
}
Combined with:
public static class ActorContextExtensions
{
public static string GetActorContext(this Actor actor)
{
var concurrencyLockProperty = GetPropertyInfo("ConcurrencyLock", typeof(ActorBase));
var concurrencyLockPropertyValue = concurrencyLockProperty.GetValue(actor);
var currentContextProperty = GetPropertyInfo("Test_CurrentContext", concurrencyLockPropertyValue.GetType());
string currentContextPropertyValue = (string)currentContextProperty.GetValue(concurrencyLockPropertyValue);
return currentContextPropertyValue;
}
private static PropertyInfo GetPropertyInfo(string propertyName, IReflect owner)
{
var property = owner.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance);
if (property == null) throw new InvalidOperationException($"Failed to find property '{propertyName}' on '{owner}'.");
return property;
}
}
Obvious downside of this, is whenever the internals of ActorBase change, you'll need to change your reflection code accordingly.

HippoCMS Rest service does not return image links

I'm a new bee to Hippo CMS. I'm working on version 10 and I'm using angularJS service to consume the rest url for "banners" I created through HippoCms.
This is my Rest URL for Banners created via Hippo's Rest Api Manager:
http://localhost:8080/site/stbetrest/Banner?_type=json
and the reponse I'm getting when consumed the link is:
{
"pageSize": 10,
"total": 3,
"items": [
{
"content": "<![CDATA[\r\n\r\n <p>Banner description</p>\r\n\r\n \r\n]]>",
"title": "Sample banner"
},
{
"content": "<![CDATA[<p>10 Simple Steps to Green Your Office:</p>\n\n<p> </p>\n\n<p>
<img src=\"/site/pagenotfound\" /></p>]]>",
"title": "10 Simple Steps to Green Your Office"
},
{
"content": "<![CDATA[<p>How to Green Your Dorm Room</p>]]>",
"title": "How to Green Your Dorm Room"
}
],
"currentPage": 1,
"totalPages": 1
}
The Problem here is I can't see the Images I used inside the banner documents in Hippo. I want to get those Images/links to load them into a carousel created in AngularJs. Please guide me how to generate the images also into the above banner response.
UPDATE:
All though via the 'localhost:8080/cms' it shows uploaded images, but can not access the image via response:
#XmlRootElement(name = "banner")
#XmlAccessorType(XmlAccessType.NONE)
#HippoEssentialsGenerated(internalName = "gogreen:bannerdocument")
#Node(jcrType = "gogreen:bannerdocument")
public class Banner extends BaseDocument {
#XmlElement
#HippoEssentialsGenerated(internalName = "gogreen:title")
public String getTitle() {
return getProperty("gogreen:title");
}
#XmlJavaTypeAdapter(HippoHtmlAdapter.class)
#XmlElement
#HippoEssentialsGenerated(internalName = "gogreen:content")
public HippoHtml getContent() {
return getHippoHtml("gogreen:content");
}
#HippoEssentialsGenerated(internalName = "gogreen:link")
public HippoBean getLink() {
return getLinkedBean("gogreen:link", HippoBean.class);
}
#XmlJavaTypeAdapter(KerkRestAdapter.class)
#XmlElement
#HippoEssentialsGenerated(internalName = "gogreen:image")
public Kerk getImage() {
return getLinkedBean("gogreen:image", Kerk.class);
}
}
and my Content rewriter is :
public class RestContentRewriter extends SimpleContentRewriter {
#Override
protected String rewriteBinaryLink(String binaryLinkSrc, Node node, HstRequestContext requestContext, Mount targetMount) {
return super.rewriteBinaryLink(binaryLinkSrc, node, requestContext, requestContext.getMount("site"));
}
And My Adapter is:
public class KerkRestAdapter extends XmlAdapter<String, HippoHtml> {
#Override
public HippoHtml unmarshal(String representation) throws Exception {
throw new UnsupportedOperationException("Unmarshalling not implemented.");
}
#Override
public String marshal(HippoHtml html) throws Exception {
if (html == null) {
return null;
}
final HstRequestContext context = RequestContextProvider.get();
//final RestContentRewriter contentRewriter = new RestContentRewriter();
final ContentReWriter<String> contentRewriter = new RestContentRewriter();
final String rewrite = contentRewriter.rewrite(html.getContent(), html.getNode(), context, context.getMount("api"));
return "<![CDATA[" + rewrite + "]]>";
}
}
additional Question: what is the mount point to be used in rewriter? ( Rest mount name or gogreen??)
Please help !
You have used the Essentials Rest setup tool. That generates a example or demo Rest implementation. After generating the setup a developer shall always want to extend and refine the result.
The links inside rich text in this setup are by default generated for the current mount, which is the rest mount. Since the images are not available through the rest mount, HST generated the /pagenotfound URL.
If you inspect the bean for the Banner you see that for the HippoHtml field an HippoHtmlAdapter is used. Check the code of it: it uses the SimpleContentRewriter. Create your own version of the Adapter and use an extended version of the ContentRewriter. Override the #rewriteBinaryLink method. In it call the super method, but specify the site mount as the target mount.
I think adding an hst:alias to the site mount is a good idea so you can reference the site with the alias.

How to translate,use JSON in GWT?

I'm new in gwt. and need to know how to use JSON in gwt so i try this simple data loader but i'm still confuse.
I create a project named 'tesdb3' in eclipse. I create the PHP side to access the database, and made the output as JSON.. I create the userdata.php in folder war. then I compile tesdb3 project. Folder tesdb3 and the userdata.php in war moved in local server(I use WAMP). I put the PHP in folder tesdb3. This is the result from my localhost/phpmyadmin/tesdb3/userdata.php
[{"kode":"002","nama":"bambang gentolet"}{"kode":"012","nama":"Algiz"}]
From that result I think the PHP side was working good.Then I create UserData.java as JSNI overlay like this:
package com.tesdb3.client;
import com.google.gwt.core.client.JavaScriptObject;
class UserData extends JavaScriptObject{
protected UserData() {}
public final native String getKode() /*-{ return this.kode; }-*/;
public final native String getNama() /*-{ return this.nama; }-*/;
public final String getFullData() {
return getKode() + ":" + getNama();
}
}
Then Finally in the tesdb3.java:
public class Tesdb3 implements EntryPoint {
String url= "http://localhost/phpmyadmin/tesdb3/datauser.php";
private native JsArray<UserData> getuserdata(String json)
/*-{
return eval(json);
}-*/;
public void LoadData() throws RequestException{
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
builder.sendRequest(null, new RequestCallback(){
#Override
public void onError(Request request, Throwable exception) {
Window.alert("error " + exception);
}
public void onResponseReceived(Request request,
Response response) {
//1
//data(getuserdata(response.getText()));
//2
JsArray<UserData> uda = JsonUtils.unsafeEval(response.getText())
data(uda);
}
});
}
public void data(JsArray<UserData> data){
for (int i = 0; i < data.length(); i++) {
String lkode =data.get(i).getKode();
String lname =data.get(i).getNama();
Label l = new Label(lkode+" "+lname);
tb.setWidget(i, 0, l);
}
RootPanel.get().add(new HTML("my data"));
RootPanel.get().add(tb);
}
public void onModuleLoad() {
try {
LoadData();
} catch (RequestException e) {
e.printStackTrace();
}
}
}
The result from both method i use in the onResponseReceived is the same. Just showing string "my data". but the method 2 create eror like this:
14:41:59.875 [ERROR] [tesdb3] Uncaught exception escaped
com.google.gwt.core.client.JavaScriptException: (SyntaxError): syntax error
Did I miss use the 2nd method? Why method 1 didn't have any eror but can't show the data?
The problem is that your JSON has incorrect syntax, you are missing a comma after the first item of the table, it should be (whitespace added for readability):
[
{
"kode": "002",
"nama": "bambang gentolet"
},
{
"kode": "012",
"nama": "Algiz"
}
]
Since JSON is a part of JavaScript this might be the syntax error exception you are getting.
PS: I'd recommend using some PHP framework to create JSON for you (Zend Framework is my usual choice). Also, JSON validators like JSONLint are very useful for debugging JSON.
It looks like a typo in your code, which brings me to naming conventions: for variables and methods use camel case, starting with a lower case character. Thus UserData UD should be UserData ud.
In your getuserdata method (which should be getUserData) you use the parameter name Json with capital J and in the native code json with the lower j. This explains the error.
Regarding the getUserData method. There is a GWT method: JsonUtils.unsafeEval(json) which you should use.
Furthermore, the code in the onResponseReceived seems incomplete, it might not be of importance as it might be incorrectly be put in this example, but just to be complete, here is what it should look like:
JsArray<UserData> uda = JsonUtils.unsafeEval(response.getText());
for (int i = 0; i < uda.length(); i++) {
UserData ud = uda.get(i);
String lKode = ud.getKode();
String lName = ud.getNama();
Label l = new Label(lKode + " " +lName);
RootPanel.get().add(l);
}