How to read uid of the CMSParagraphComponent on the View - sap-commerce-cloud

Here is what I exactly want to achieve but didn't have an answer. What's best practice to get the uid of CMSParagraphComponent on the storefront?
DefaultCMSComponentService
protected Collection<String> getEditorProperties(AbstractCMSComponentModel component, boolean readableOnly) {
String code = component.getItemtype();
if (!this.cEditorProperties.containsKey(code)) {
LOG.debug("caching editor properties for CMSComponent [" + component.getItemtype() + "]");
List<String> props = new ArrayList();
Collection<String> systemProps = this.getSystemProperties(component);
Set<AttributeDescriptorModel> attributeDescriptors = this.getTypeService()
.getAttributeDescriptorsForType(this.getTypeService().getComposedTypeForCode(code));
Iterator var8 = attributeDescriptors.iterator();
while (true) {
AttributeDescriptorModel ad;
String qualifier;
do {
do {
if (!var8.hasNext()) {
this.cEditorProperties.put(code, props);
return (Collection) this.cEditorProperties.get(code);
}
ad = (AttributeDescriptorModel) var8.next();
qualifier = ad.getQualifier();
} while (systemProps.contains(qualifier));
} while (readableOnly && !ad.getReadable());
props.add(qualifier);
}
} else {
return (Collection) this.cEditorProperties.get(code);
}
}
public Collection<String> getSystemProperties(AbstractCMSComponentModel component) {
String code = component.getTypeCode();
if (!this.cSystemProperties.containsKey(code)) {
LOG.debug("caching system properties for CMSComponent [" + component.getTypeCode() + "]");
List props = null;
try {
props = (List) Registry.getApplicationContext().getBean(code + "SystemProperties");
} catch (NoSuchBeanDefinitionException var5) {
LOG.debug("No bean found for : " + code + "SystemProperties", var5);
props = this.getSystemProperties();
}
this.cSystemProperties.put(code, props);
}
return (Collection) this.cSystemProperties.get(code);
}
It's not being populated because it's considered as the system property. Hence, as per the above logic system property will not be consider as redable property.
Now question is, How hybris get the list of system property for the given type? In another words, where this Registry.getApplicationContext().getBean(code + "SystemProperties") bean declare?
EDIT: The fact I know is, if Property attribute of AttributeDescriptor is set to false then it consider as system property. But when I checked for uid AttributeDescriptor, it (Property attribute) already set true.

There are some functionalities oob in hybris that uses the uid inside a view. For example the SearchPageController. To be more specific, let's take a look at this method :
private static final String COMPONENT_UID_PATH_VARIABLE_PATTERN = "{componentUid:.*}";
...
#ResponseBody
#RequestMapping(value = "/autocomplete/" + COMPONENT_UID_PATH_VARIABLE_PATTERN, method = RequestMethod.GET)
public AutocompleteResultData getAutocompleteSuggestions(...){
final SearchBoxComponentModel component = (SearchBoxComponentModel) cmsComponentService.getSimpleCMSComponent(componentUid);
}
The actual COMPONENT_UID_PATH_VARIABLE_PATTERN value is in the searchboxcomponent.jsp :
<spring:url value="/search/autocomplete/{/componentuid}" var="autocompleteUrl" htmlEscape="false">
<spring:param name="componentuid" value="${component.uid}"/>
</spring:url>
How does this work? Every time you type something, a call to this endpoint is made, with the component uid extracted using ${component.uid}.
Why does this work? Let's take a look at the productLayout1Page.jsp and take a simple tag from there:
<cms:pageSlot position="CrossSelling" var="comp" element="div" class="productDetailsPageSectionCrossSelling">
<cms:component component="${comp}" element="div" class="productDetailsPageSectionCrossSelling-component"/>
</cms:pageSlot>
Now we see that there is a <cms:component component=${..}.../> tag which reference a component instance and you can access it using ${component.attributeName} inside the component's jsp.

It seems not easy to get the uid on the storefront. But I have managed it like this
Create a dynamic attribute and return uid value from the getter method
Now you can populate this dynamic attribute on the frontend

Related

Dapper Extensions custom ClassMapper isn't called on Insert()

I'm using Dapper Extensions and have defined my own custom mapper to deal with entities with composite keys.
public class MyClassMapper<T> : ClassMapper<T> where T : class
{
public MyClassMapper()
{
// Manage unmappable attributes
IList<PropertyInfo> toIgnore = typeof(T).GetProperties().Where(x => !x.CanWrite).ToList();
foreach (PropertyInfo propertyInfo in toIgnore.ToList())
{
Map(propertyInfo).Ignore();
}
// Manage keys
IList<PropertyInfo> propsWithId = typeof(T).GetProperties().Where(x => x.Name.EndsWith("Id") || x.Name.EndsWith("ID")).ToList();
PropertyInfo primaryKey = propsWithId.FirstOrDefault(x => string.Equals(x.Name, $"{nameof(T)}Id", StringComparison.CurrentCultureIgnoreCase));
if (primaryKey != null && primaryKey.PropertyType == typeof(int))
{
Map(primaryKey).Key(KeyType.Identity);
}
else if (propsWithId.Any())
{
foreach (PropertyInfo prop in propsWithId)
{
Map(prop).Key(KeyType.Assigned);
}
}
AutoMap();
}
}
I also have this test case to test my mapper:
[Test]
public void TestMyAutoMapper()
{
DapperExtensions.DapperExtensions.DefaultMapper = typeof(MyClassMapper<>);
MySubscribtionEntityWithCompositeKey entity = new MySubscribtionEntityWithCompositeKey
{
SubscriptionID = 145,
CustomerPackageID = 32
};
using (var connection = new SqlConnection(CONNECTION_STRING))
{
connection.Open();
var result = connection.Insert(entity);
var key1 = result.SubscriptionID;
var key2 = result.CustomerPackageID;
}
}
Note that I set the default mapper in the test case.
The insert fails and I notive that my customer mapper is never called. I have no documentation on the github page on the topic, so I'm not sure if there's anything else I need to do to make dapper extensions use my mapper.
Thanks in advance!
Looking at your question, you are attempting to write your own defalut class mapper derived from the existing one. I never used this approach; so I do not know why it is not working or whether it should work.
I explicitly map the classes as below:
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
}
public sealed class CustomerMapper : ClassMapper<Customer>
{
public CustomerMapper()
{
Schema("dbo");
Table("Customer");
Map(x => x.CustomerID).Key(KeyType.Identity);
AutoMap();
}
}
The AutoMap() will map rest of the properties based on conventions. Please refer to these two resources for more information about mapping.
Then I call SetMappingAssemblies at the startup of the project as below:
DapperExtensions.DapperExtensions.SetMappingAssemblies(new[] { Assembly.GetExecutingAssembly() });
The GetExecutingAssembly() is used in above code because mapping classes (CustomerMapper and other) are in same assembly which is executing. If those classes are placed in other assembly, provide that assembly instead.
And that's it, it works.
To set the dialect, I call following line just below the SetMappingAssemblies:
DapperExtensions.DapperExtensions.SqlDialect = new DapperExtensions.Sql.SqlServerDialect();
Use your preferred dialect instead of SqlServerDialect.
Apparently, the solution mentioned here may help you achieve what you are actually trying to. But, I cannot be sure, as I said above, I never used it.

JAX-RS: How to automatically serialize a collection when returning a Response object?

I have a JAXB-annotated employee class:
#XmlRootElement(name = "employee")
public class Employee {
private Integer id;
private String name;
...
#XmlElement(name = "id")
public int getId() {
return this.id;
}
... // setters and getters for name, equals, hashCode, toString
}
And a JAX-RS resource object (I'm using Jersey 1.12)
#GET
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/")
public List<Employee> findEmployees(
#QueryParam("name") String name,
#QueryParam("page") String pageNumber,
#QueryParam("pageSize") String pageSize) {
...
List<Employee> employees = employeeService.findEmployees(...);
return employees;
}
This endpoint works fine. I get
<employees>
<employee>
<id>2</id>
<name>Ana</name>
</employee>
</employees>
However, if I change the method to return a Response object, and put the employee list in the response body, like this:
#GET
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/")
public Response findEmployees(
#QueryParam("name") String name,
#QueryParam("page") String pageNumber,
#QueryParam("pageSize") String pageSize) {
...
List<Employee> employees = employeeService.findEmployees(...);
return Response.ok().entity(employees).build();
}
the endpoint results in an HTTP 500 due to the following exception:
javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException: A message body writer for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/xml was not found
In the first case, JAX-RS has obviously arranged for the proper message writer to kick in when returning a collection. It seems somewhat inconsistent that this doesn't happen when the collection is placed in the entity body. What approach can I take to get the automatic JAXB serialization of the list to happen when returning a response?
I know that I can
Just return the list from the resource method
Create a separate EmployeeList class
but was wondering whether there is a nice way to use the Response object and get the list to serialize without creating my own wrapper class.
You can wrap the List<Employee> in an instance of GenericEntity to preserve the type information:
http://docs.oracle.com/javaee/6/api/javax/ws/rs/core/GenericEntity.html
You can use GenericEntity to send the collection in the Response. You must have included appropriate marshal/unmarshal library like moxy or jaxrs-jackson.
Below is the code :
#GET
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/")
public Response findEmployees(
#QueryParam("name") String name,
#QueryParam("page") String pageNumber,
#QueryParam("pageSize") String pageSize) {
...
List<Employee> employees = employeeService.findEmployees(...);
GenericEntity<List<Employee>> entity = new GenericEntity<List<Employee>>(Lists.newArrayList(employees))
return Response.ok().entity(entity).build();
}
I resolved this issue by extending the default JacksonJsonProvider class, in particular method writeTo.
Analyzing the source code of this class I found the block where the actual type is instantiated by reflection, so I've modified the source code as below:
public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,Object> httpHeaders, OutputStream entityStream) throws IOException {
/* 27-Feb-2009, tatu: Where can we find desired encoding? Within
* HTTP headers?
*/
ObjectMapper mapper = locateMapper(type, mediaType);
JsonEncoding enc = findEncoding(mediaType, httpHeaders);
JsonGenerator jg = mapper.getJsonFactory().createJsonGenerator(entityStream, enc);
jg.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
// Want indentation?
if (mapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) {
jg.useDefaultPrettyPrinter();
}
// 04-Mar-2010, tatu: How about type we were given? (if any)
JavaType rootType = null;
if (genericType != null && value != null) {
/* 10-Jan-2011, tatu: as per [JACKSON-456], it's not safe to just force root
* type since it prevents polymorphic type serialization. Since we really
* just need this for generics, let's only use generic type if it's truly
* generic.
*/
if (genericType.getClass() != Class.class) { // generic types are other impls of 'java.lang.reflect.Type'
/* This is still not exactly right; should root type be further
* specialized with 'value.getClass()'? Let's see how well this works before
* trying to come up with more complete solution.
*/
//**where the magic happens**
//if the type to instantiate implements collection interface (List, Set and so on...)
//Java applies Type erasure from Generic: e.g. List<BaseRealEstate> is seen as List<?> and so List<Object>, so Jackson cannot determine #JsonTypeInfo correctly
//so, in this case we must determine at runtime the right object type to set
if(Collection.class.isAssignableFrom(type))
{
Collection<?> converted = (Collection<?>) type.cast(value);
Class<?> elementClass = Object.class;
if(converted.size() > 0)
elementClass = converted.iterator().next().getClass();
//Tell the mapper to create a collection of type passed as parameter (List, Set and so on..), containing objects determined at runtime with the previous instruction
rootType = mapper.getTypeFactory().constructCollectionType((Class<? extends Collection<?>>)type, elementClass);
}
else
rootType = mapper.getTypeFactory().constructType(genericType);
/* 26-Feb-2011, tatu: To help with [JACKSON-518], we better recognize cases where
* type degenerates back into "Object.class" (as is the case with plain TypeVariable,
* for example), and not use that.
*/
if (rootType.getRawClass() == Object.class) {
rootType = null;
}
}
}
// [JACKSON-578]: Allow use of #JsonView in resource methods.
Class<?> viewToUse = null;
if (annotations != null && annotations.length > 0) {
viewToUse = _findView(mapper, annotations);
}
if (viewToUse != null) {
// TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
ObjectWriter viewWriter = mapper.viewWriter(viewToUse);
// [JACKSON-245] Allow automatic JSONP wrapping
if (_jsonpFunctionName != null) {
viewWriter.writeValue(jg, new JSONPObject(this._jsonpFunctionName, value, rootType));
} else if (rootType != null) {
// TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
mapper.typedWriter(rootType).withView(viewToUse).writeValue(jg, value);
} else {
viewWriter.writeValue(jg, value);
}
} else {
// [JACKSON-245] Allow automatic JSONP wrapping
if (_jsonpFunctionName != null) {
mapper.writeValue(jg, new JSONPObject(this._jsonpFunctionName, value, rootType));
} else if (rootType != null) {
// TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
mapper.typedWriter(rootType).writeValue(jg, value);
} else {
mapper.writeValue(jg, value);
}
}
}

Model-Identifier for Node in JavaFX 2 TreeItem

Is there a way to store an identifier of a model object or the model object itself in a JavaFX 2 TreeItem<String>? There is just Value to store the text...
I'm populating a TreeView from a list of model objects, and need to find it when the user clicks a node. I'm used to work with Value and Text in .NET Windows Forms or HTML and I am afraid I cannot adapt this way of thinking to JavaFX...
You can use any objects with TreeView, they just have to override toString() for presenting or extend javafx.scene.Node
E.g. for next class:
private static class MyObject {
private final String value;
public MyObject(String st) { value = st; }
public String toString() { return "MyObject{" + "value=" + value + '}'; }
}
TreeView should be created next way:
TreeView<MyObject> treeView = new TreeView<MyObject>();
TreeItem<MyObject> treeRoot = new TreeItem<MyObject>(new MyObject("Root node"));
treeView.setRoot(treeRoot);
I have the same issue as the OP. In addition I want to bind the value displayed in the TreeItem to a property of the object. This isn't complete, but I'm experimenting with the following helper class, where I'm passing in the "user object" (or item) to be referenced in the TreeItem, and a valueProperty (which, in my case, is a property of the item) to be bound to the TreeItem.value.
final class BoundTreeItem<B, T> extends TreeItem<T> {
public BoundTreeItem(B item, Property<T> valueProperty) {
this(item, valueProperty, null);
}
public BoundTreeItem(B item, Property<T> valueProperty, Node graphic) {
super(null, graphic);
itemProperty.set(item);
this.valueProperty().bindBidirectional(valueProperty);
}
public ObjectProperty<B> itemProperty() {
return itemProperty;
}
public B getItem() {
return itemProperty.get();
}
private ObjectProperty<B> itemProperty = new SimpleObjectProperty<>();
}

Using Dapper.Net ORM, how do I cast stored procedure output to a concrete type?

Using Entity Framework I can create concrete classes from most of the sprocs in the database of a project I'm working on. However, some of the sprocs use dynamic SQL and as such no metadata is returned for the sproc.
So for a that sproc, I manually created a concrete class and now want to map the sproc output to this class and return a list of this type.
Using the following method I can get a collection of objects:
var results = connection.Query<object>("get_buddies",
new { RecsPerPage = 100,
RecCount = 0,
PageNumber = 0,
OrderBy = "LastestLogin",
ProfileID = profileID,
ASC = 1},
commandType: CommandType.StoredProcedure);
My concrete class contains
[DataContractAttribute(IsReference=true)]
[Serializable()]
public partial class LoggedInMember : ComplexObject
{
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int16 RowID
{
get
{
return _RowID;
}
set
{
OnRowIDChanging(value);
ReportPropertyChanging("RowID");
_RowID = StructuralObject.SetValidValue(value);
ReportPropertyChanged("RowID");
OnRowIDChanged();
}
}
private global::System.Int16 _RowID;
partial void OnRowIDChanging(global::System.Int16 value);
partial void OnRowIDChanged();
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String NickName
{
get
{
return _NickName;
}
set
{
OnNickNameChanging(value);
ReportPropertyChanging("NickName");
_NickName = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("NickName");
OnNickNameChanged();
}
}
private global::System.String _NickName;
partial void OnNickNameChanging(global::System.String value);
partial void OnNickNameChanged();
.
.
.
Without having to iterate through the results and add the output parameters to the LoggedInMember object, how do I map these on the fly so I can return a list of them through a WCF service?
If I try var results = connection.Query<LoggedInMember>("sq_mobile_get_buddies_v35", ... I get the following error:
System.Data.DataException: Error parsing column 0 (RowID=1 - Int64)
---> System.InvalidCastException: Specified cast is not valid. at Deserialize...
At a guess your SQL column is a bigint (i.e. Int64 a.k.a. long) but your .Net type has a n Int16 property.
You could play around with the conversion and ignore the stored procedure by doing something like:
var results = connection.Query<LoggedInMember>("select cast(9 as smallint) [RowID] ...");
Where you are just selecting the properties and types you want to return your object. (smallint is the SQL equivalent of Int16)
The solution to this was to create a complex object derived from the sproc with EF:
public ProfileDetailsByID_Result GetAllProfileDetailsByID(int profileID)
{
using (IDbConnection connection = OpenConnection("PrimaryDBConnectionString"))
{
try
{
var profile = connection.Query<ProfileDetailsByID_Result>("sproc_profile_get_by_id",
new { profileid = profileID },
commandType: CommandType.StoredProcedure).FirstOrDefault();
return profile;
}
catch (Exception ex)
{
ErrorLogging.Instance.Fatal(ex); // use singleton for logging
return null;
}
}
}
In this case, ProfileDetailsByID_Result is the object that I manually created using Entity Framework through the Complex Type creation process (right-click on the model diagram, select Add/Complex Type..., or use the Complex Types tree on the RHS).
A WORD OF CAUTION
Because this object's properties are derived from the sproc, EF has no way of knowing if a property is nullable. For any nullable property types, you must manually configure these by selecting the property and setting its it's Nullable property to true.

Get job title using System.DirectoryServices.AccountManagement

I've successfully used the AccountManagement code to retrieve basic AD information but it's only returning a very limited set of information about the returned object. How can I get extended information from AD using the AccountManagement functionality. Specifically the Job Title or title as it seems to be called in my instance of AD.
I know how to do it using the older DirectoryServices but I'd like to know how to do it using the new namespace.
Yes, the default set of properties on UserPrincipal is quite limited - but the great part is: there's a neat extensibility story in place!
You need to define a class descending from UserPrincipal and then you can very easily get access to a lot more properties, if needed.
The skeleton would look something like this:
namespace ADExtended
{
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("User")]
public class UserPrincipalEx : UserPrincipal
{
// Inplement the constructor using the base class constructor.
public UserPrincipalEx(PrincipalContext context) : base(context)
{ }
// Implement the constructor with initialization parameters.
public UserPrincipalEx(PrincipalContext context,
string samAccountName,
string password,
bool enabled) : base(context, samAccountName, password, enabled)
{}
UserPrincipalExSearchFilter searchFilter;
new public UserPrincipalExSearchFilter AdvancedSearchFilter
{
get
{
if (null == searchFilter)
searchFilter = new UserPrincipalExSearchFilter(this);
return searchFilter;
}
}
// Create the "Title" property.
[DirectoryProperty("title")]
public string Title
{
get
{
if (ExtensionGet("title").Length != 1)
return string.Empty;
return (string)ExtensionGet("title")[0];
}
set { ExtensionSet("title", value); }
}
// Implement the overloaded search method FindByIdentity.
public static new UserPrincipalEx FindByIdentity(PrincipalContext context, string identityValue)
{
return (UserPrincipalEx)FindByIdentityWithType(context, typeof(UserPrincipalEx), identityValue);
}
// Implement the overloaded search method FindByIdentity.
public static new UserPrincipalEx FindByIdentity(PrincipalContext context, IdentityType identityType, string identityValue)
{
return (UserPrincipalEx)FindByIdentityWithType(context, typeof(UserPrincipalEx), identityType, identityValue);
}
}
}
And that's really almost all there is! The ExtensionGet and ExtensionSet methods allow you to "reach down" into the underlying directory entry and grab out all the attributes you might be interested in....
Now, in your code, use your new UserPrincipalEx class instead of UserPrincipal:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// Search the directory for the new object.
UserPrincipalEx myUser = UserPrincipalEx.FindByIdentity(ctx, "someUserName");
if(myUser != null)
{
// get the title which is now available on your "myUser" object!
string title = myUser.Title;
}
}
Read all about the System.DirectoryServices.AccountManagement namespace and its extensibility story here:
Managing Directory Security Principals in the .NET Framework 3.5
Update: sorry - here's the UserPrincipalExSearchFilter class - missed that one in the original post. It just shows the ability to also extend the search filters, if need be:
public class UserPrincipalExSearchFilter : AdvancedFilters
{
public UserPrincipalExSearchFilter(Principal p) : base(p) { }
public void LogonCount(int value, MatchType mt)
{
this.AdvancedFilterSet("LogonCount", value, typeof(int), mt);
}
}
To Augment the above I have knocked up an extension method to call ExtensionGet. It uses reflection to get hold of the protected method you would otherwise have to inherit. You might need to use this if you are returning UserPrincipalObjects from Groups.Members, for example
public static class AccountManagmentExtensions
{
public static string ExtensionGet(this UserPrincipal up, string key)
{
string value = null;
MethodInfo mi = up.GetType()
.GetMethod("ExtensionGet", BindingFlags.NonPublic | BindingFlags.Instance);
Func<UserPrincipal, string, object[]> extensionGet = (k,v) =>
((object[])mi.Invoke(k, new object[] { v }));
if (extensionGet(up,key).Length > 0)
{
value = (string)extensionGet(up, key)[0];
}
return value;
}
}
There are simpler ways of getting to that info. Here is the way I got to Job Title in VB.NET:
Dim yourDomain As New PrincipalContext(ContextType.Domain, "yourcompany.local")
Dim user1 As UserPrincipal = UserPrincipal.FindByIdentity(yourDomain, principal.Identity.Name)
Dim Entry As DirectoryServices.DirectoryEntry = user1.GetUnderlyingObject()
Dim JobTitle As String = Entry.Properties.Item("Title").Value.ToString
To expand on Programmierus' comment, here is a simple way to do this on the fly in C#.
public static string GetProperty(UserPrincipal userPrincipal, string property)
{
DirectoryEntry d = (DirectoryEntry)userPrincipal.GetUnderlyingObject();
return d.Properties[property]?.Value?.ToString();
}

Resources