I have installed a particular module in my SilverStripe installation. The following is the directory structure
- Root
- framework
- cms
- mymodule
- code
- extensions
- CustomClass.php
Here is an example of CustomClass.php
class CustomClass extends Extension {
public function init() {
}
public function customMethod() {
}
}
I need to override the customMethod method of the CustomClass class. I could easily change this method, but changing here will cause trouble in the future if the modules get updated. All the changes made will be lost.
So for this I want to extend the extension class used in modules.
I have created an extension /mysite/extensions/MyCustomClass.php
class MyCustomClass extends Extension {
public function customMethod() {
//do my code here
}
}
but I have no idea how to apply this. I thought CustomClass::add_extension("MyCustomClass ") but surely this will not work because add_extension method doesn't exist in CustomClass.
How do we cope with this scenario? Can I use Injector instead? If yes, how can it be called in mysite/_config.php instead of _config.yml?
Using injector does solve the problem but have to use _config.yml as well. Here is what I did.
File /mysite/extensions/MyCustomClass.php
class MyCustomClass extends CustomClass {
public function customMethod() {
//do my code here
}
}
in /mysite/_config/config.yml I added following lines
Injector:
CustomClass:
class: MyCustomClass
And in /mysite/_config.php I added following line
$object = Injector::inst()->create('CustomClass');
And it all worked fine.
There is another way to achieve similar functionality without straight up replacing a previous extension. With SilverStripe's extension system, we can control not only what configuration settings are loaded but the order they are loaded. This is important to note because the customMethod function from an extension, it uses the first one it finds from all the extensions loaded.
Because of this, it can be only a matter of controlling when your MyCustomClass extension is loaded so you can have your own customMethod function run.
Let's say the "MyModule" module has the following YAML file defined:
---
Name: MyModuleExtensions
After:
- 'framework/*'
- 'cms/*'
---
Page:
extensions:
- CustomClass
All we need to do is specify a separate YAML file to run before this "MyModule" one. This can be accomplished like:
---
Name: MyCustomModule
Before:
- MyModule/#MyModuleExtensions
---
Page:
extensions:
- MyCustomClass
Now, when you call your customMethod function on whatever class you have your extensions on (so in my example, the Page class), it will call the one from your MyCustomClass extension.
Related
Firstly, I tried all the questions & answers related to this topic. Additionally and I tried related questions and try to solve it but no success. So please read my question thoroughly.
i refer below Link this topic but not getting solution
override-core-files-using-module-prestashop
override-controller-in-prestashop-1-6
prestashop-translating-overrided-controller
prestashop-1-6-controller-override
i want to this override prestashopbundle controller using my custom module.
my Custom module structure
- mymodule
- controllers
- admin
- prestashop
- override
- views
i want to this controller override
prestashop/src/prestashopbundle/controller/Admin/sell/Customer/CustomerController.php
mymodule path
module/mymodule/
Thanks
After Long Time research in Prestashop after then i got best Solution
Prestashop(1.7.*) say :
As Controllers are not available for override and can be regarded as
internal classes, we don’t consider moving a Controller in another
namespace as a backward-compatibility break.
Controller and Routing
After i created a custom Hook register my custom Module. inside custom hook implement functionality and DONE it :)
public function hookActionViewCustomers($params)
{
$creddit = Configuration::get('Credit_Checkbox_1');
$customerid =$params['request']->attributes->get('customerId');
$Status_HTMl =$this->Get_Status_HTML($customerid);
if(!$creddit)
{
// Checkbox Checked
$ButtonDisable =" <button type=\"submit\" disabled='disabled' title='Not Allow'> Check Status </button>";
}
else{
// Checkbox unChecked
$ButtonDisable =" <button type=\"submit\" title='Click Get Status'> Check Status </button>";
}
return $Status_HTMl.$ButtonDisable;
}
I Use Orchard 1.10.1 CMS. I have created a widget module in code. In the ResourceManifest file I added this code
public void BuildManifests(ResourceManifestBuilder builder)
{
var manifest = builder.Add();
manifest.DefineStyle("ShareButtons").SetUrl("ShareButtons.css").SetDependencies("font-awesome.css");
}
and in template cshml I added this code
#{
Style.Require("ShareButtons");
}
Problem is in this case file font-awesome.css won't come in the page's source files.
what am I doing wrong here?
And when I use this code in template it works fine and font-awesome.css will come in the page's source files
#{
Style.Include("font-awesome.css");
Style.Include("ShareButtons.css");
}
ps: In the Style folder of my module project I have ShareButtons.css and font-awesome.css.
Your setup is wrong. You must either register a depency yourself (just like your ShareButton-Style), or in your case use the correct FontAwesome resource from Orchard.Resources.
So your code would look like this:
manifest.DefineStyle("ShareButtons")
.SetUrl("ShareButtons.css")
.SetDependencies("FontAwesome");
Here's the setup of FontAwesome you're requiring as dependency from Orchard.Resources:
namespace Orchard.Resources {
public class FontAwesome : IResourceManifestProvider {
public void BuildManifests(ResourceManifestBuilder builder) {
var manifest = builder.Add();
manifest.DefineStyle("FontAwesome").SetUrl("font-awesome.min.css", "font-awesome.css").SetVersion("4.4.0").SetCdn("//maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css", "//maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.css", true);
}
}
}
Q1.I want to count the unread messages before every page rendered ,and add the number into twig template.i don't know how to make it
Q2.i have read the symfony tutorial,it seems that i will make a service ,but i don't know how to do it.is the following code right? and i don't know what to write at the seconde argument
namespace App\RepairBundle\Service;
use Symfony\Component\DependencyInjection\ContainerInterface;
use App\RepairBundle\Entity\RepairMessageRepository;
class CountUnReadMessage
{
protected $container;
protected $messageRepository;
public function __construct(ContainerInterface $container,RepairMessageRepository $messageRepository)
{
$this->container = $container;
$this->messageRepository = $messageRepository;
}
public function countUnRead()
{
$number = $this->messageRepository->countByUnread($this->container->get('security.token_storage')->getToken()->getUser()->getId());
return $number;
}
}
parameters:
app.repair.count_unread_message: App\RepairBundle\Service\CountUnReadMessage
services:
app.repair.count_unread_message:
class: %app.repair.count_unread_message%
arguments:
- #service_container
- #
If piece of the twig template containing message counter similar in all templates, you can move it to separate template and call service inside this template. You steps to achieve this might look like this:
Write service for getting message counter (you almost got it, but try to avoid injecting whole container in servce since it is a very bad practice. In this case, i think you could inject only security.token_storage)
Make this service visible in twig templates by declare it in config file.
config.yml
twig:
globals:
count_read_message: #app.repair.count_unread_message
In your separate twig file call this service
message_block.html.twig
{{ count_read_message.countUnRead() }}
And include this twig file to needed template (better idea would be keep main template for most of templates and include you file in this template, but this dependenced of template structure)
I hope you got the main idea =)
P.S. Answering for Q2 - it is #doctrine.orm.entity_manager
If you want to inject repository make another service with your repository:
app.message_repository:
class: Doctrine\ORM\EntityRepository
factory: ["#doctrine.orm.default_entity_manager", getRepository]
arguments:
- App\RepairBundle\Entity\RepairMessage
Then in your service:
app.repair.count_unread_message:
class: %app.repair.count_unread_message%
arguments:
- #service_container
- #app.message_repository
BTW you don't need container, inject only security.token_storage instead of container.
I am a beginner with AngularJS and I have a little problem, I installed grunt-contrib-less to support less files instead css but now I have to declare all less styles that will be compiled into only one css file.
But my problem is normally when I'm using less, I write some code for a specific page, and here I have to write the style code for all pages. This is confusing and not really maintanable so is there a best practice to organize less styles?
I tought that there may be multiple solution:
Apply a class to body tag and change it with I don't know what
(controller, services or other)
(Import LESS file only for one page)
Generate multiple css file depending which style is compiled (but I can't do this because I can't configure grunt correctly)
Do this with DOM manipulation (but it don't find it beautifull because I think Angular must have a good way to solve that problem)
Could you explain me how to have different style for differents views ? I don't want to have the same style for all links in all views and without create hundreds classes I don't know how to do that.
Use directive
and add whatever variables/code/logic you want to add
HTML template(directive) of style can be added to your view and after compile you will get different ui for all your views
for reference read
angular directive
I solve my problem by adding specific class on body tag depending the route.
I put a variable in rootScope called 'pageStyle' with correspond to the classname that I want. This variable is updated automatically when route change (see run function). There is an event when the route change ($stateChangeSuccess or $routeChangeSuccess depending if you are using ngRoute or -angularui-routeur).
In my case i would like to add the name of the route but you can do it with the controller name or something else.
Here is an example
This is the routes :
angular
.module('frontApp', [])
.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider, $mdThemingProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: '../views/home.html',
controller: function ($scope) {
$scope.msg = 'Xavier';
}
})
.state('form', {
url: '/form',
templateUrl: '../views/form.html',
controller: 'FormCtrl'
});
}])
And in the run function you will see the event bound to adapt the class when route change :
.run(function($rootScope) {
$rootScope.pageStyle = '';
// Watch state and set controller name in pageStyle variable when state change
$rootScope.$on('$stateChangeSuccess', function(event, toState) {
event.preventDefault();
if (toState && toState.name && typeof toState.name === 'string'){
$rootScope.pageStyle = toState.name;
} else {
$rootScope.pageStyle = '';
}
});
});
Extra informations :
Note that the event called when route change is different if you are using ngroute. use "$routeChangeSuccess" if you use ngRoute and "$stateChangeSuccess" if you choose to use angular-ui-routeur
If you want to add the controller name instead the route name simply use the follow and replace 'ctrl' with you controller suffixe:
if (toState && toState.controller && typeof toState.controller !== 'function'){
$rootScope.pageStyle = toState.controller.toLowerCase().replace('ctrl','');
}
Hope it help someone else
I built a custom module that manages appointments for a service-based company. All of the current functionality is contained in the admin section. I have not used a single ContentItem or ContentPart. All the models are just plain records.
I'm looking to create a widget to expose the ability to sign up for an appointment from the front end. I have a partial view and a controller that handles the display and form submit, but I'm not sure how to tie that into a widget that can be placed in one of the content zones of the front-end.
I've spent quite a bit of time researching this, and can't find a good path to follow. (I've tried a few and got sub-optimal results)
Any suggestions?
The best answer for me was to create a widget Type definition in the migration.cs file of the module:
ContentDefinitionManager.AlterTypeDefinition("CreateAppointmentWidget",
cfg => cfg
.WithPart("WidgetPart")
.WithPart("CommonPart")
.WithSetting("Stereotype", "Widget"));
Then create a handler for that widget at /MyModule/Handlers/CreateAppointmentWidgetHandler.cs:
public class CreateAppointmentWidgetHandler : ContentHandler
{
private readonly IRepository<FieldTechRecord> _repository;
public CreateAppointmentWidgetHandler(IRepository<FieldTechRecord> repository)
{
_repository = repository;
}
protected override void BuildDisplayShape(BuildDisplayContext context)
{
base.BuildDisplayShape(context);
if (context.ContentItem.ContentType == "CreateAppointmentWidget")
{
CreateAppointmentViewModel model = new CreateAppointmentViewModel(_repository.Fetch(x => x.IsActive));
context.Shape.AppointmentModel = model;
}
}
}
Then create a matching widget template at /MyModule/Views/Widget-CreateAppointmentWidget.cshtml that inserts the Partial View:
#Html.Partial("CreateAppointment", (MyModule.Models.Views.CreateAppointmentViewModel)Model.AppointmentModel)
The above code grabs the partial view /MyModule/Views/CreateAppointment.cshtml.
Thanks to Giscard's suggestion, I was able to correct the links rendered from CreateAppointment.cshtml by using #Url.RouteUrl() and defining named routes to point where I needed the action and ajax requests to go.
The nice thing about this solution is that it provided a way to create the widget without having to rework my models to use Orchards ContentPart functionality.
Something is not connecting in my head, because I have been able to create a theme with zones, and then dispatch a shape from my module into that zone without much more than doing #Display.Shape(). So I am curious if it's absolutely necessary to use a handler to override the BuildDisplayShape.
Again, this is in the scenario where you have models as plain records (not using ContentItem or ContentPart - and even if not using them, you've shown an example of creating one through migrations).
Something like this - Controller:
public ShapeResult MyShape()
{
var shape = _orchardServices.New.MyPath1_MyShape();
return new ShapeResult(this, shape);
}
Then create a MyShape.cshtml shape with whatever code I have (no need for example).
NOTE: I use a custom IShapeTemplateHarvester file which adds paths where I can store my shapes (instead of using "Views", "Views/Items", "Views/Parts", "Views/Fields", which is the stock in Orchard). It goes something like this:
NB: I hate that code doesn't automatically wrap in SO.
[OrchardSuppressDependency("Orchard.DisplayManagement
.Descriptors.ShapeTemplateStrategy.BasicShapeTemplateHarvester")]
public class MyShapeTemplateHarvester : BasicShapeTemplateHarvester,
IShapeTemplateHarvester
{
public new IEnumerable<string> SubPaths()
{
var paths = base.SubPaths().ToList();
paths.Add("Views/MyPath1");
paths.Add("Views/MyPath2");
return paths;
}
}
Say I have Index.cshtml in my Theme. I have two choices (I use both and use the Theme as the default presentation).
Index.cshtml in Theme folder:
#*Default Content*#
Index.cshtml in Module folder:
#*Special Content overriding Theme's Index.cshtml*#
Display.MyPath1_MyShape()
Even better for me is that I can do this in the Index.cshtml in Theme folder:
#*Whatever content*#
Display.MyPath1_MySecondShape()
There is no ~/MyPath1/MySecondShape.cshtml in the Theme, but there is one in the Module, which the Theme displays! This is great because I can have a special Theme and have multiple modules (that are placed on separate sites) go back and forth with the theme (think Dashboard for different services in the same profession on different sites).
NOTE: The above may only be possible with IThemeSelector implementation such as:
public class MyThemeSelector : IThemeSelector
{
public ThemeSelectorResult GetTheme(RequestContext context)
{
if (MyFilter.IsApplied(context))
{
return new ThemeSelectorResult { Priority = 200,
ThemeName = "MyDashboard" };
}
return null;
}
}
Just my two bits.