EPiServer 7: Trouble adding dojo module to user interface - dojo

I have some trouble adding my dojo module to the user interface.It tries to access in the episerver/shell.
I have added a module.conifg;
<module>
<assemblies>
<add assembly="Mobile.Web" />
</assemblies>
<dojoModules>
<add name="MobileWeb" path="Scripts" />
</dojoModules>
</module>
Added my dojo module at ~/ClientResources/js/KeyValueEditor.js,
named the module declare('MobileWeb.js.KeyValueEditor', [widget, templatedMixin] and in my block type:
[ClientEditor(ClientEditingClass = "MobileWeb.js.KeyValueEditor")]
public virtual string KeyValueCategoryData { get; set; }
It works sometimes, but when I changed the dojoModules -> add name attribute to MobileWeb, it wont work anymore.
Anyone knows what this can be?

It looks like the system don’t know where to find client resources.
The name in the dojoModules node is kind of your namespace and the path should point to the folder where Dojo can find resources/scripts for that namespace. This path is relative to your module root directory.
As I understand you probably want to put your JavaScript files in ClientResources/js subfolder and your styles in ClientResources/css subfolder inside your module directory. In this case you can define Dojo module like this:
<dojoModules>
<add name="MobileWeb" path="ClientResources/js" />
</dojoModules>
It means that system will try to find resources in ClientResources/js subfolder in your module directory. When declaring widgets you should follow your namespace and folder structure. You can declare your widget in ClientResources/js/KeyValueEditor.js file like this:
define([
// your imports
],
function (/* imports */) {
return declare("MobileWeb.KeyValueEditor", [_Widget, /* … */ ], {
// implementation
});
});
And then you can use MobileWeb.KeyValueEditor name when you reference your widget in the back-end C# code.
You can find some examples and source code in sample add-on for EPiServer 7.

Related

asp core: where the "Shared" folder is configured and how to change its path?

This line in cshtml
<partial name="_CookieConsentPartial" />
suppose to search for _CookieConsentPartial.cshtml in \Pages\Shared folder.
Could I configure web app "to search for _CookieConsentPartial" in \Pages folder?
I have deleted Shared folder and moved _CookieConsentPartial.cshtml to \Pages folder but after this <partial name="_CookieConsentPartial" /> stop working - <partial name="\_CookieConsentPartial.cshtml" /> works - but this is what I want to avoid.
For configuring additional search folder path, you could configure RazorViewEngineOptions like:
services.Configure<RazorViewEngineOptions>(options => {
options.PageViewLocationFormats.Add("/Pages/Shared-1/{0}.cshtml");
});
By default, PageViewLocationFormats already defined /Pages/{0}.cshtml
// Remarks:
// Locations are format strings (see https://msdn.microsoft.com/en-us/library/txafckwd.aspx)
// which may contain the following format items:
// {0} - View Name {1} - Page Name
// Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions.PageViewLocationFormats
// work in tandem with a view location expander to perform hierarchical path lookups.
// For instance, given a Page like /Account/Manage/Index using /Pages as the root,
// the view engine will search for views in the following locations: /Pages/Account/Manage/{0}.cshtml
// /Pages/Account/{0}.cshtml /Pages/{0}.cshtml /Pages/Shared/{0}.cshtml /Views/Shared/{0}.cshtml
public IList<string> PageViewLocationFormats { get; }

ViewComponent in external assembly cannot be found

I am using the latest VS.2017 updates and templates for an MVC .NET Core web application. I decided I wanted ViewComponents in an external assembly since I read several posts that indicated it was not possible without odd tricks.
I have my main web application and then I created a .NET Framework class library named MySite.Components which is the "external assembly". In it I installed the ViewFeatures NuGet. I created my View component CSHTML in its /Views/Shared/Components/GoogleAdsense/Default.cshtml.
I noticed that my CSPROJ already has the GoogleAdSense as an embedded resource:
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
<EmbeddedResource Include="Views\Shared\Components\GoogleAdsense\Default.cshtml" />
</ItemGroup>
The view component is actually quite simple:
namespace MySite.Components.ViewComponents {
[ViewComponent(Name = "GoogleAdsense")]
public class GoogleAdsense : ViewComponent {
public async Task<IViewComponentResult> InvokeAsync(string adSlot, string clientId, string adStyle = "")
{
var model = await GetConfigAsync(adSlot, clientId, adStyle);
return View(model);
}
private Task<GoogleAdUnitCompModel> GetConfigAsync(string adSlot, string clientId, string adStyle)
{
GoogleAdUnitCompModel model = new GoogleAdUnitCompModel
{
ClientId = clientId, // apparently we can't access App_Data because there is no AppDomain in .NET core
SlotNr = adSlot,
Style = adStyle
};
return Task.FromResult(model);
}
}
}
Then in the main project (the ASP.NET Core web application) I installed the File Provider NuGet and modified my Startup:
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProviders.Add(new EmbeddedFileProvider(
typeof(MySite.Components.ViewComponents.GoogleAdsense).GetTypeInfo().Assembly,
"MySite.Components.ViewComponents"
));
});
Then I try to use the view component in a view like this:
#using MySite.Components.ViewComponents
:
#Component.InvokeAsync(nameof(GoogleAdsense), new { adSlot = "2700000000", clientId = "ca-pub-0000000000000000", adStyle="" })
And I get an error saying
*InvalidOperationException: A view component named 'GoogleAdsense' could not be found.*
Also tried using the notation without nameof() that uses a generic parameter for InvokeAsync but that fails too but with
*"Argument 1: cannot convert from 'method group' to 'object'"*
And using the TagHelper form simply renders it as an unrecognized HTML:
<vc:GoogleAdsense adSlot = "2700000000" clientId = "ca-pub-0000000000000000"></vc:GoogleAdsense>
Finally, on the Main Assembly (the actual web application) I used the GetManifestResourceNames() on the external assembly type to verify it was embedded and the returned list had it listed as:
[0] = "MySite.Components.Views.Shared.Components.GoogleAdsense.Default.cshtml"
I did a lot of trial-and-error and was finally able to get this working. There's a number of guides on this, but they're all for .NET Core 1.0, and I also found they did not work when using a DLL reference from another solution.
Let's talk about component name first. The component name is determined either by convention or attribute. To name by convention, the class name must end in "ViewComponent", and then the component name will be everything prior to "ViewComponent" (just like Controller names work). If you just decorate the class with [ViewComponent], the component name will explicitly be the class name. You can also directly set the name to something else with the attribute's Name parameter.
All three of these examples produce a component name of "GoogleAdsense".
public class GoogleAdsenseViewComponent : ViewComponent { }
[ViewComponent]
public class GoogleAdsense : ViewComponent { }
[ViewComponent(Name = "GoogleAdsense")]
public class Foo: ViewComponent { }
After that, be sure your views are in the proper folder structure.
├── Views
│ ├── Shared
│ │ ├── Components
│ │ │ ├── GoogleAdsense <--component name
│ │ │ │ ├── Default.cshtml
Then, the Views must all be included as embedded resources. Right-click > Properties on the view and set the Build Action to "Embedded resource". You can also do this manually in the .csproj (and take advantage of globbing if you have a lot of Views).
<ItemGroup>
<EmbeddedResource Include="Views\Shared\Components\GoogleAdsense\Default.cshtml" />
</ItemGroup>
That's it for the source project. Make note that you must do a build for any changes to your views to show up, since they are being included in the DLL. This seems obvious, but it's a change from how you normally interact with views.
Now to the consuming project. In ConfigureServices in Startup.cs, you must add your component's assembly as both an MVC ApplicationPart and as an EmbeddedFileProvider. The EmbeddedFileProvider gives access to the views embedded in the assembly, and the ApplicationPart sets up MVC to include it in its search paths.
var myAssembly = typeof(My.External.Project.GoogleAdsenseViewComponent).Assembly;
services.AddMvc().AddApplicationPart(myAssembly);
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProviders.Add(new EmbeddedFileProvider(myAssembly, "My.External.Project"));
});
If you have multiple ViewComponents in that assembly, this will suffice for all of them. You can optionally provide a base namespace to EmbeddedFileProvider. I have found times when it was necessary and times when it was not, so it is best to just provide it. This namespace should be the Default Namespace property of your project (Properties -> Application -> Default Namespace).
Finally, to invoke the ViewComponent, use the component name. Remember that the component name may differ from the class name. Unless you used [ViewComponent] to set the component name to be the class name, you cannot use nameof.
#await Component.InvokeAsync("GoogleAdsense")
I was able to get a ViewComponent working from an external solution by generating and installing a NuGet package from the "external" assembly into the consuming solution with no problem. I had originally tried to add a reference to the dll without creating my own NuGet package and it did not work.
I'd recommend trying the NuGet package first. If it still doesn't work, can you post both projects so I can help debug?

How and where to instantiate a custom class that extends the WP_REST_Controller

I have a plugin that I created and I want to use the WP rest api controller pattern in order to extend the api.
<?php
/**
* Plugin Name: myplugin
* Plugin URI: h...
* Description: A simple plugin ...
* Version: 0.1
* Author: Kamran ...
* Author ....
* License: GPL2
function myplugin_register_endpoints(){
require_once 'server/controllers/my_ctrl.php';
$items=new items();
$items->register_routes();
}
add_action('rest_api_init','myplugin_register_endpoints');
.
.
I created a class a folder called server/controllers and inside it my_ctrl.php file with a class that extends WP_REST_Controller that looks like this
// server/controllers/my_ctrl.php
class items extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {
.....
}
}
However I am receiving the following error in sublime xdebuge call stack:
[Fatal error] Class 'myplugin\WP_REST_Controller' not found
I am not sure how to solve this issue, where to put the files for my custom controller, where to create the instance of the custom class etc?
Stumbled upon this and thought I'd provide my solution in case someone else encounters this.
The idea is to postpone the instantiation of the class extending WP_REST_Controller by not instantiating it until the actual rest_api_init hook is called.
Code example:
add_action( 'rest_api_init', function () {
require_once(plugin_dir_path(__FILE__) . '/VideoImageApi.php');
VideoImageApi::instance()->register_routes();
});
Note the require_once from within the callback.
I have manged to solve the issue,
I checked the wp-content\plugins folder and I couldn't find the \rest-api folder and although I found the folder inside \wp-includes\rest-api it seems that this folder that integrates the "wp rest api" into core doesn't include all the classes that the api can expose (it includes only 3 php files), So it didn't include \wp-content\plugins\rest-api\lib\endpoints\class-wp-rest-controller.php . I installed the "wp rest api" plugin and it was added to wp-content\plugins and now I don't have the error anymore. (It was strange because I don't know when it was deleted from my project)
Thank you Dan your comments really helped me to recheck everything and scan the folders included in my wordpress and realize that the plugin is missing and that the folder \wp-includes\rest-api doesnt contain all the needed classes.

Override and use front controller features in a prestashop module [1.6.x.x]

I would like to edit and add features to the prestashop Store Locator page.
Prestashop's documentation isn't really clear, and i would like to know if it's possible to implement a Controller in a custom module.
I would like to create a module which is able to extends StoreFrontController and it's features without starting from scratch.
Is it possible ? Have you some documentation for me ?
A beginner,
Best.
As you have many requirements, you will have to go with an override of class StoresController.php.
Your module folder should look like this:
/mymodule
/mymodule.php
/config.xml
/override
/controllers
/front
StoresController.php
/views
/templates
/front
stores.tpl
In StoresController.php you will have to override initContent():
<?php
class StoresController extends StoresControllerCore
{
/**
* Assign template vars related to page content
* #see FrontController::initContent()
*/
public function initContent()
{
parent::initContent();
// here add any smarty variables you want
$this->setTemplate(_PS_MODULE_DIR_.'mymodule/views/templates/front/stores.tpl');
}
}
Now you can add as many variables as you want in this controller and customized its template in your own module.
We you create an override in a module, it will be only parsed once at installation. If your module is already installed you will have to uninstall it and install it again. Then your override file will be copied to the root /override folder.
Any change made in your module override will not be reflected to the root override folder. So you will have to uninstall and install your module each time you want to make a change.
So I advise you to make all your changes directly in the root override folder, and when you're done copy this file back into your module folder. And if you don't want to uninstall your module and install it again to declare this file, you can put it directly in the root override folder and delete the file /cache/class_index.php so that Prestashop knows that an overrides has been added.
If you have any questions :)
You can start by overriding front controller like
`"/modules/mymodule/override/controllers/front/StoresController.php" and in this fine add class "class StoresControllerCore extends FrontController {
public function initContent()
{
parent::initContent();
//here do whatever you like
}
}"
though you must know coding to proceed further.

asp.net mvc-4 and grunt-uglify

How to manage .min files generated by grunt-uglify and "debug" version?
If I set
BundleTable.EnableOptimizations = true;
or at web.config
<compilation debug="false" />
apparently the bundle concat all files by itself and don't use the min files generated by grunt.
All debug version has their own minify version at the same folder ex:
Folder A
testA.js
testA.min.js
...
Folder B
testB.js
testB.min.js
...
PS: I'm not referencing minified files in bundleConfig.cs.
What is the best solution to handle it? I need to use ONLY minified files generated by GRUNT at the release moment, and still using debug version when in development.
BundleTable.EnableOptimizations = true;
This code works only if you're using BundleConfig.cs
I think that the best way for you is to create a custom UrlHelper that can build JS scripts url according to if you're in debug mode or not (this is pseudo-code) :
public static class UrlHelper
{
public static string JsScript(this UrlHelper urlHelper, string baseFileName) {
return HttpContext.Current.IsDebuggingEnabled
? urlHelper.Content(baseFileName + '.js')
: urlHelper.Content(baseFileName + '.min.js');
}
}
And for example, if you want to use it in your Razor view :
<script src="#Url.JsScript("~/js/folderA/testA")"></script>