Use NewClassWizard in kentico admin - kentico

I want to create a custom page that allows me to track manually every data modification made by kentico in the database
I understood that I can implement it by code by using the NewClassWizard. But when done how I can use it in Kentico admin ?
I try to load it from Page Type module but can find a way to specify my custom class
Is it the right way to use that? The documentation is not really clear on that
thanks for your help

I'd suggest creating a global event handler to track changes. While Kentico will already do a lot of this tracking for you, you can specifically add whatever specific events you want as a global event. This will handle any changes made on the customer-facing public website, inside the Kentico UI, and in any Kentico API calls.
In your custom global event handler, you can check for different types of events based on the object and the CRUD activity. For instance something like this:
using CMS;
using CMS.CustomTables;
using CMS.DataEngine;
using CMS.DocumentEngine;
// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomInitializationModule))]
public class CustomInitializationModule : Module
{
// Module class constructor, the system registers the module under the name "CustomInit"
public CustomInitializationModule()
: base("CustomInit")
{
}
// Contains initialization code that is executed when the application starts
protected override void OnInit()
{
base.OnInit();
// Assigns custom handlers to events
DocumentEvents.Insert.After += Document_Insert_After;
}
private void Document_Insert_After(object sender, DocumentEventArgs e)
{
if (e.Node.ClassName.ToLower() == "your.classname")
{
// create an event to log to a custom module table or a custom table so it's not logged directly in the Kentico Event log
CustomTableItem item = new CustomTableItem("customtable.name");
item.SetValue("ItemEventType", "Create document");
item.SetValue("ItemEventCode", "Your event code");
item.SetValue("ItemEventUserID", e.Node.DocumentModifiedByUserID);
item.SetValue("ItemEventUrl", e.Node.NodeAliasPath);
item.SetValue("ItemEventName", e.Node.DocumentName);
item.Insert();
}
}
}
This is a simple example of when a document is created. You don't have to check the page type's classname if you don't want to. You can also include a global event handler for any "object" like a cms_class object, just use that definition in your code <name>Info.TYPEINFO.Events.<eventtype>. This is not limited to any object, you can track transformation updates, page type field modifications, etc. You just need to know the object name/type and write code around it.

Related

Create a listener for every form submission in Liferay 7.2

I want to create a listener for every form submission in my portal.
I create a form called "Test Form" and before (or after) liferay saves informations in local database I want to send the data form to external API (and do whatever is needed).
I was trying to accomplish this task by doing something similar to this:
import org.osgi.service.component.annotations.Component;
import com.liferay.portal.kernel.exception.ModelListenerException;
import com.liferay.portal.kernel.model.BaseModelListener;
import com.liferay.portal.kernel.model.Layout;
import com.liferay.portal.kernel.model.ModelListener;
#Component(immediate = true, service = ModelListener.class)
public class FormListener extends BaseModelListener<Layout> {
#Override
public void onBeforeCreate(Layout model) throws ModelListenerException {
System.out.println(
"About to create layout: " + model.getNameCurrentValue());
model.setTitle("Title generated by model listener!");
}
}
But change Layout for a class related to every form submission - something like FormContent or something like that. Would be posible to accomplish this with "onAfterSubmit" listener (or something like that) or there's a better solution?
Thanks in advance.
You can do this frontend side like Marco Mercuri shows you but backend side ModelListener is your friend so you are on the right track. For this you probably need to watch for DDLRecord. Chances are you need both the ModelListener and MessageListener since the data might not be available realtime. As an example you can look here: https://github.com/jverweijL/auto-translator/tree/master/auto-translator-listener/src/main/java/com/liferay/demo/auto/translate
If you want to do something whenever any form is submitted, you can just use a script with an event listener.
Create a Liferay theme: Theme documentation
In your main.js file (or wherever you want) put a javascript/jQuery/... event listener on form submit, something like this:
$('form').on('submit',function(){
//do what you need...
});
jQuery event listener documentation
PS
The code you wrote is a Model Listener on Layout (that are the pages of our site in Liferay "language"). The method onBeforeCreate you posted is executed just before a new page is created, so one time per page. It has nothing to do with forms and submit.

Orchard CMS: How to cancel a remove event

I have created some code to disallow deletion of a Content Item if that item has any localized Dependencies. To do this I created a new "extension" interface and (default)class within Orchard.Framework.ContentManagement, and then created a new class within the Orchard.Localization modul, using the SuppressDependency attribute. It returns a simple Boolean value based on a query to see if there are any ContentItems that list the current item as their MasterContentItem
This is called from the AdminController within Orchard.Core.Contents, and if it returns true then I add a message and redirect to the returnUrl, otherwise the removal continues as normal.
All of this is working perfectly.
HOWEVER, I would like to refactor this to take advantage of the existing infrastructure with IContentManager and add a Removing() method to the LocalizationPartHandler.
My question is - is it possible to "cancel" the remove action based on output of ContentHandler invoked earlier...
As mentioned in my comment I'm not completely sure as to what you want to accomplish, but my guess is that you want to create your own module and within that module create a Handler implementation in which you do your thing...
From what I understand is that you want to hook into the Remove event lifecycle. Handlers provide the OnRemoving which executes before the item is removed. I'm not an expert on handlers and their extension points and how you could cancel the event, but my guess is that you want to implement the following:
public class YourCustomHandler : ContentHandler {
public YourCustomHandler(IYourCancelService yourCancelService) {
OnRemoving<LocalizationPart>((context, part) =>
yourCancelService.CancelRemoveEvent(context));
}
}
Read this for more info on handlers: http://docs.orchardproject.net/Documentation/Understanding-content-handlers

Orchard: Welding a ContentPart to all ContentTypes

I have a requirement where i need to weld a ContentPart to all the content types. Please guide me what is the best place to write this code.
I looked into the Orchard source code where InfosetPart is being welded with all content types in ContentHandlerBase's Activating method.
Following the InfosetPart weld mechanism i created one class inheriting from ContentHandlerBase and in Activating method i placed a break point with following condition which is getting hit again and again (more than once for one content type)
context.ContentType == "Page"
I'm not sure if it should be as it is because ideally it should hit this condition only once.
The way you are implementing it is correct. Your code is executed multiple times because content handlers are invoked for each content item, and not just for the content type. This also allows you to weld your part to only some of you content items, not all items of a specified type.
You wrote that you created a subclass of ContentHandlerBase. You should use ContentHandler as a base class.
Below is a simple code example how this should be done.
public class MyPartHandler : ContentHandler
{
protected override void Activating(ActivatingContentContext context) {
context.Builder.Weld<MyPart>();
}
}

MVVMCross changing selected tab bar item from within nested view controller

We're using MVVMCross within our application and I've come up against something that I'm not sure I've solved in the best way possible.
One of our ViewModels contains 3 other view models - a dashboard and 2 lists. In iOS this is presented using a MvxTabBarViewController which works great. Android and WP present this view in a similar manner. An example of the object model is below:
public class ProjectViewModel : MvxViewModel
{
public DashboardViewModel Dashboard {get;set;}
public FirstListViewModel FirstList {get;set;}
public SecondListViewModel SecondList {get;set;}
}
We're now in the situation where if a certain action happens within the DashboardViewModel we would like to instruct the navigation to change the tab in iOS and the same thing to happen on the other platforms.
The only way I've been able to get the tab to change on iOS is to use this.SelectedIndex = 1; from within the iOS ProjectView.
At the moment also the only way I've managed to trigger this change is to fire an event from the DashboardViewModel and then the ProjectViewModel subscribes to this and fires another event which is subscribed to by the ProjectView to instruct it to change the tab in whatever device specific way it needs to. I can't help but think there is a better way to do this.
I've tried taking a look at a custom ViewPresenter for iOS and calling ShowViewModel FirstListViewModel from within the DashboardViewModel but the presenter doesn't appear to be getting used so we just transition normally. My idea was I could get in the middle, cancel the navigation request and then flip the active tab on the ProjectView.
Any suggestions would be appreciated on how we could do this in a better cross platform way using MVVMCross to handle the change if at all possible.
You should be able to do this in any of several ways:
using a custom presenter with overridden Show as you suggest
using a custom presenter with overridden ChangePresentation - and using a custom hint
using a custom binding or a binding to a property within the ProjectView to drive the transition
using a custom IMvxInteraction property
using a custom event from VM to View
using a messenger to send a message from the ViewModels to the Views.
Ultimately lots of these could work and which of these I might choose would depend on which one worked and which one the team are happy with - shipping the working app is always the ultimate goal.
Given where I am with MvvmCross experience, I'd probably opt today for trying the approach of trying a custom IMvxInteraction property. But this might not be for everyone... it certainly might be overkill for this sample...
However, to do this, I would try:
add a public enum Display { Dash, First, Second } to the Core project
add a ProjectViewModel property:
private MvxInteraction<Display> _display = new MvxInteraction< Display >();
public IMvxInteraction<Display> DisplayChange { get { return _display; } }
whenever this ViewModel wants to fire the change it can fire it using e.g. _display.Raise(Display.First)
the ProjectView could then bind Display to its own property which might be implemented like:
private IDisposable _subscription;
private IMvxInteraction<Display> _displayInteraction;
public IMvxInteraction<Display> ChangeDisplay
{
get { return _displayInteraction; }
set
{
if (_subscription != null)
{
_subscription.Dispose();
_subscription = null;
}
_displayInteraction = value;
if (_displayInteraction != null)
{
_subscription = _displayInteraction.WeakSubscribe(DoDisplayChange);
}
}
}
private void DoDisplayChange(Display which)
{
// change the tab display here
}
the binding would be added in ViewDidLoad like:
set.Bind(this).For(v => v.ChangeDisplay).To(vm => vm.DisplayChange);

How can I extract the main body of an Orchard Page via a Url

What I'm looking to do is have 2 views of an orchard page.
The first will include headers and footers, the second just the main body content.
The reason is so that we can maintain the body in one place, and it will be used either as a stand alone site, or just embedded within another.
I'm thinking that it would be done by accessing the page using a different route, or appending a querystring parameter.
Any other options are welcomed.
The method I am about to describe is arguably a hack and may go against some of the intentions of the creators of Orchard, but it will get the job done.
Orchard uses the ThemeFilter with the Themed attribute to decide whether the current controller action's output will be 'themed' --- i.e., be displayed with headers and footers. The controller used by Orchard to display content items has this attribute enabled, which is why content items are displayed themed. What you are asking to do is to suppress this 'themed' mode based on the presence of a query string parameter.
ThemeFilter kicks in at a very early stage of the page request and applies itself by setting a value in the current request's http context.
ThemeFilter.cs:
public static void Apply(RequestContext context) {
// the value isn't important
context.HttpContext.Items[typeof (ThemeFilter)] = null;
}
This class does not provide a way of unApplying this value. If you are willing to modify the Orchard source code, you may add this method yourself:
public static void Unapply(RequestContext context) {
context.HttpContext.Items.Remove(typeof (ThemeFilter));
}
Then you can simply create your own action filter that checks for the existence of a query string and then call this method if appropriate, something like:
using System.Web.Mvc;
using Orchard.Mvc.Filters;
using Orchard.Themes;
namespace Demo {
public class UnthemeFilter : FilterProvider, IActionFilter {
public void OnActionExecuting(ActionExecutingContext filterContext) {
}
public void OnActionExecuted(ActionExecutedContext filterContext) {
if (filterContext.RequestContext.HttpContext.Request["unthemed"] != null) {
ThemeFilter.Unapply(filterContext.RequestContext);
}
}
}
}
Now by adding ?unthemed=true, you will be able to suppress the theming.
If you are unwilling or unable to modify the Orchard source code, it is still possible to do the same thing by directly removing the typeof (ThemeFilter) from the HTTP context in your filter. However, this breaks encapsulation and should probably be avoided in object-oriented programming.

Resources