How to get LanguageManager class in SlingModels? - aem

I am declaring LanguageManager class but not getting how to instantiate it.
ResourceResolver res = resource.getResourceResolver();
**LanguageManager languagemanager= null;**
final PageManager pageManager = resource.getResourceResolver().adaptTo(
PageManager.class);
final Page currentPage = pageManager.getContainingPage(resource);
String currentPagePath = currentPage.getPath();
SlingBindings bindings = (SlingBindings) request
.getAttribute(SlingBindings.class.getName());
for (final Page page : languagemanager.getLanguageRoots(res,
currentPagePath)) {
languagePages.put(page.getLanguage(false), page);
}

There is an OSGi service (com.day.cq.wcm.core.impl.LanguageManagerImpl) that implements the LanguageManager interface, so you should be able to just reference it with the #Inject annotation from within your Sling model.
#Inject
private final LanguageManager languageManager;

Related

How to pass a parameter to a Mobx Controller (Flutter)

I am an Android Developer and new to Flutter. I really like the way Mobx works, because it remembers me Android's ViewModel. By the way, when I create a ViewModel, I like to create it passing the repository as a parameter, so I can test it with different Data Sources (ie. local or cloud).
So, this is my class right now.
import 'package:mobx/mobx.dart';
part 'create_bill_controller.g.dart';
class CreateBillController = _CreateBillControllerBase
with _$CreateBillController;
abstract class _CreateBillControllerBase with Store {
final appBarTitle = 'Criar Conta';
final criarConta = 'Criar conta';
final nomeDaConta = 'Nome da conta';
final seuNome = 'Seu nome';
#action
createBill(String billname, String userName) {
// here, dataSource should be given in a constructor
datasource.createBill(billName, userName);
}
}
How can I pass a DataSource (repository) as a parameter to this class?
What you need is to declare constructor for CreateBillController instead of _CreateBillControllerBase, because constructor is not inherited by child class in Dart. The simplest way is to assign the passed in datasource to the corresponding property in parent class in the constructor, as you can see in the below snippet. You can also implement a constructor for _CreateBillControllerBase as well and call super(datasource) in CreateBillController's constructor.
import 'package:mobx/mobx.dart';
part 'create_bill_controller.g.dart';
class CreateBillController extends _CreateBillControllerBase with _$CreateBillController {
// HERE! Implement constructor for CreateBillController
// Do this if you have a constructor for _CreateBillControllerBase
//
// CreateBillController(DataSource datasource) : super(datasource)
//
CreateBillController(DataSource datasource) {
super.datasource = datasource;
}
}
abstract class _CreateBillControllerBase with Store {
final appBarTitle = 'Criar Conta';
final criarConta = 'Criar conta';
final nomeDaConta = 'Nome da conta';
final seuNome = 'Seu nome';
// HERE! Declare datasource
DataSource datasource;
#action
createBill(String billname, String userName) {
datasource.createBill(billName, userName);
}
}

Return a custom response when using the Authorize Attribute on a controller

I have just implemented the Bearer token and I have added the Authorize attribute to my controller class and that works fine. It looks like this:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
What I would like to do is to create a more complex response from the server when it fails, rather then the standard 401.
I tried filters but they are not invoked at all.
Any ideas how to do this?
Have a custom scheme, custom authorization handler and poof!
Notice that I injected the Handler in ConfigureServices:
services.AddAuthentication(options =>
{
options.DefaultScheme = ApiKeyAuthenticationOptions.DefaultScheme;
options.DefaultAuthenticateScheme = ApiKeyAuthenticationOptions.DefaultScheme;
})
.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(
ApiKeyAuthenticationOptions.DefaultScheme, o => { });
ApiKeyAuthenticationOptions
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
public const string DefaultScheme = "API Key";
public string Scheme => DefaultScheme;
public string AuthenticationType = DefaultScheme;
public const string HeaderKey = "X-Api-Key";
}
ApiKeyAuthenticationHandler
/// <summary>
/// An Auth handler to handle authentication for a .NET Core project via Api keys.
///
/// This helps to resolve dependency issues when utilises a non-conventional method.
/// https://stackoverflow.com/questions/47324129/no-authenticationscheme-was-specified-and-there-was-no-defaultchallengescheme-f
/// </summary>
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
private readonly IServiceProvider _serviceProvider;
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options, ILoggerFactory logger,
UrlEncoder encoder, ISystemClock clock, IServiceProvider serviceProvider)
: base (options, logger, encoder, clock)
{
_serviceProvider = serviceProvider;
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var token = Request.Headers[ApiKeyAuthenticationOptions.HeaderKey];
if (string.IsNullOrEmpty(token)) {
return Task.FromResult (AuthenticateResult.Fail ("Token is null"));
}
var customRedisEvent = _serviceProvider.GetRequiredService<ICustomRedisEvent>();
var isValidToken = customRedisEvent.Exists(token, RedisDatabases.ApiKeyUser);
if (!isValidToken) {
return Task.FromResult (AuthenticateResult.Fail ($"Invalid token {token}."));
}
var claims = new [] { new Claim ("token", token) };
var identity = new ClaimsIdentity (claims, nameof (ApiKeyAuthenticationHandler));
var ticket = new AuthenticationTicket (new ClaimsPrincipal (identity), Scheme.Name);
return Task.FromResult (AuthenticateResult.Success (ticket));
}
}
Focus on the handler class. Apart from the sample code I've provided, simply utilise the base class properties like Response to set your custom http status code or whatever you may need!
Here's the derived class if you need it.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhandler-1?view=aspnetcore-3.1

Why is onChanged() not called when observing LiveData

This question is a follow up to this problem here: How to make retrofit API call using ViewModel and LiveData
The mistakes 1 and 2 highlighted in that post's response have been fixed. For mistake 3, I haven't yet moved the API call to a repository, but I will once the code start working properly.
So I'm trying to make an API call using Retrofit, using MVVM with LiveData and ViewModel. The API call (which currently is in the ViewModel), is working properly, but the changes is not being picked up by the Observer in the Activity.
I've setup my ViewModel observer as follow:
public class PopularGamesActivity extends AppCompatActivity {
private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
private static final String FIELDS = "id,name,genres,cover,popularity";
private static final String ORDER = "popularity:desc";
private static final int LIMIT = 30;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_popular_games);
PopularGamesViewModel popViewModel = ViewModelProviders.of(this).get(PopularGamesViewModel.class);
popViewModel.getGameList().observe(this, new Observer<List<Game>>() {
#Override
public void onChanged(#Nullable List<Game> gameList) {
String firstName = gameList.get(0).getName();
Timber.d(firstName);
}
And my ViewModel code is as follow:
public class PopularGamesViewModel extends AndroidViewModel {
private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
private static final String FIELDS = "id,name,genres,cover,popularity";
private static final String ORDER = "popularity:desc";
private static final int LIMIT = 30;
public PopularGamesViewModel(#NonNull Application application) {
super(application);
}
public LiveData<List<Game>> getGameList() {
final MutableLiveData<List<Game>> gameList = new MutableLiveData<>();
// Create the retrofit builder
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(igdbBaseUrl)
.addConverterFactory(GsonConverterFactory.create());
// Build retrofit
Retrofit retrofit = builder.build();
// Create the retrofit client
RetrofitClient client = retrofit.create(RetrofitClient.class);
Call<List<Game>> call = client.getGame(FIELDS,
ORDER,
LIMIT);
call.enqueue(new Callback<List<Game>>() {
#Override
public void onResponse(Call<List<Game>> call, Response<List<Game>> response) {
Timber.d("api call sucesss");
if (response.body() != null) {
Timber.d("First game: " + response.body().get(0).getName());
gameList.setValue(response.body());
}
}
#Override
public void onFailure(Call<List<Game>> call, Throwable t) {
Timber.d("api call failed");
}
});
return gameList;
}
}
When I run the code, the onResponse in the ViewModel class will output the correct response from the API call, so the call is working properly. But the onChanged() in the PopularGamesActivity class will never get called. Can someone shed some light on what I'm doing wrong? Thank you!
Ok, so this turned out to be a weird android studio bug. I was initially running the code on my real nexus 4 device, and the onChange never gets called. However, after running it on an emulated device, it started working immediately. And now, it's working on my real device too.
I don't know the actual reason behind it, but if anyone in the future run into a problem where onChange won't get called, try switching device/emulators.
Cheers.
I debug the code on a device and have the same problem.
M'n solution was simply activate the device screen.
The screensaver was the problem..

How to share data between a page and a component in AEM 6.2?

Is there a way how I can call the same instance of a model within HTL using the same data?
I want to create an object within the model of a page, let's say a String object, and then use it in the model of a component.
To create the bean (or model instance), I call
<sly data-sly-use.model="myModel"/>
in the page and in the component
Problem is that I have now 2 instances with 2 sets of local data - what I do NOT want to have.
The SlingHttpServletRequest (in general) provides an instance of SlingBindings, which contains a reference to "currentPage" (I am using the static field WCMBindings.CURRENT_PAGE [dependency: groupId: com.adobe.cq.sightly, artifactId: cq-wcm-sightly-extension, version: 1.2.30] in my example).
The Optional I am using in my example is a Java 8 class which can be used to avoid too many checks for null references.
final Optional<Page> optional = Optional.ofNullable(request)
.map(req -> (SlingBindings) req.getAttribute(SlingBindings.class.getName()))
.map(b -> (Page) b.get(WCMBindings.CURRENT_PAGE));
A simplified/explicit example would be
Page getCurrentPageFromRequest(#Nonnull final SlingHttpServletRequest request) {
final SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName())
if (bindings == null) {
return null;
}
return (Page) bindings.get(WCMBindings.CURRENT_PAGE);
}
In your Sling model you would just call
#Model(adaptables={ SlingHttpServletRequest.class, })
public class Model {
public Model(#Nonnull final SlingHttpServletRequest request) {
final Page currentPage = getCurrentPageFromRequest(request);
// read properties.
}
Page getCurrentPageFromRequest(#Nonnull final SlingHttpServletRequest request) {
final SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName())
if (bindings == null) {
return null;
}
return (Page) bindings.get(WCMBindings.CURRENT_PAGE);
}
}

How to Import Java classes and Alloy-UI tag library in Dynamic Datalist (DDL) Freemarker/Velocity Display Template (Liferay 6.2)

Is there any way to Import Java classes and Alloy-UI tag library into a Liferay 6.2 Dynamic Data List (DDL) Freemarker/Velocity Display Template?
For example, when editing the Display Template of a Dynamic Data List portlet (DDL), is there any way to import a class such as WorkflowConstants.java and use it?
Also is there any way to use add dependencies for alloy-ui tags with Display templates?
Thank you!
You can access static fields,static methods and non-static methods from freemarker template. Here is sample Program.
Custom constant class but you have WorkflowConstants in life ray you can access same
public class WorkflowConstants {
public static int ACTION_SAVE_DRAFT = 1;
public static String CONTEXT_COMPANY_ID = "MTRX_78";
// static method
public static String test() {
return "executed Constant#test()";
}
}
//FooCallMethod class
public class FooCallMethod {
public static void main(String[] args) throws Exception {
Configuration config = new Configuration();
config.setClassForTemplateLoading(FooCallMethod.class, "");
config.setObjectWrapper(new DefaultObjectWrapper());
config.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
Map<String, Object> dataModel = new HashMap<String, Object>();
TemplateHashModel staticModels = BeansWrapper.getDefaultInstance().getStaticModels();
dataModel.put("statics", staticModels);
TemplateHashModel fileStatics = (TemplateHashModel) staticModels
.get("java.io.File");
Cal cal = new Cal();
dataModel.put("cal", cal);
dataModel.put("File", fileStatics);
Template template = config.getTemplate("/foo.ftl");
StringWriter out = new StringWriter();
template.process(dataModel, out);
System.out.println(out.getBuffer().toString());
}
}
Cal.java
public class Cal {
public int add(int a, int b) {
return a + b;
}
public int mul(int a, int b) {
return a * b;
}
}
Template
${statics["com.tset.WorkflowConstants"].test()}
${statics["com.tset.WorkflowConstants"].ACTION_SAVE_DRAFT}
${statics["com.tset.WorkflowConstants"].CONTEXT_COMPANY_ID}
2+3 = ${cal.add(2,3)}
10/2 = ${cal.mul(10,2)}
${statics["java.lang.System"].currentTimeMillis()}
Note:
You can easily use Dynamic Data Lists in freeMarker tempalte
see here it is mentioned in velocity but need to use freeMarker syntax.
Use freemaker language: more powerfull and full support of taglib, velocity, it's too poor to use taglib
https://www.liferay.com/it/web/mika.koivisto/blog/-/blogs/using-freemarker-in-your-theme-templates
Look also freemaker guide