Workflow extension that doesn't run - acumatica

In an Acumatica code extension, I am attempting to create a workflow extension for BusinessAccountWorkflow. It adds a few actions that I want to suppress. My extension’s Configure method override basically doesn’t do anything, so that the base method doesn’t create actions. My override method doesn’t seem to be running, though, because the actions still appear, and my breakpoint isn’t hit. Below is the extension. What could I be missing to get this override to run?
public class BusinessAccountWorkflowExt : PXGraphExtension<BusinessAccountWorkflow,
BusinessAccountMaint>
{
public static bool IsActive() => false;
public override void Configure(PXScreenConfiguration configuration)
{
var context = configuration
.GetScreenConfigurationContext<BusinessAccountMaint, BAccount>();
context.AddScreenConfigurationFor(screen =>
{
return screen;
});
//context.RemoveScreenConfigurationFor();
}
}

Tony, your code sample sets IsActive to false which should disable the graph extension. This doesn't exactly seem to behave the same on workflows as it does normal graph extensions, so I'm not sure if it causes any harm.
Next, I think you really want to use UpdateScreenConfigurationFor instead of AddScreenConfigurationFor. This lets you tap into the defined workflow and add actions or alter conditions. For instance, you can update an action to be .IsHiddenAlways() if you don't want it to show in any condition. (Alternatively, you can hide it via permissions and never have to code for that!)
Take a look at standard workflow source code that ends _ApprovalWorkflow.cs for examples of how Acumatica updates an existing workflow to insert Approve and Reject functionality as well as altering transitions to inject the Pending approval state.
To be able to add your own actions, it's pretty simple code. Below is an example of how I injected my own actions into the menu for the Sales Order Entry screen, which honestly has a crazy complex workflow overall. However, always adding my buttons to the menu doesn't require touching any of that standard complexity.
using PX.Data;
using PX.Data.WorkflowAPI;
using SSCS;
namespace PX.Objects.SO.Workflow.SalesOrder
{
public class SOOrderEntry_Workflow_SSCS : PXGraphExtension<SOOrderEntry>
{
public static bool IsActive() => true; // Insert your own logic here
#region Initialization
public override void Configure(PXScreenConfiguration config)
{
Configure(config.GetScreenConfigurationContext<SOOrderEntry, SOOrder>());
}
protected virtual void Configure(WorkflowContext<SOOrderEntry, SOOrder> context)
{
context.UpdateScreenConfigurationFor(screen =>
{
return screen
.WithActions(actions =>
{
actions.Add<SOOrderEntry_Extension>(g => g.RecordOutage, a => a.WithCategory(PredefinedCategory.Actions));
});
});
}
#endregion
}
}
Where I added actions in the above sample using actions.Add, you would want to use actions.Update to alter the definition of the action. This is where you would put .IsHiddenWhen(condition) or .IsHiddenAlways().

Related

How to customize the approval button on the Expense Claim screen

I am trying to customize the approve button and I can't find the right method to do it.
Could you tell me how I can manipulate that event.
Thanks in advance.
button to customize
Method
I looked around at the ExpenseClaimEntry, and the approval is added from the class ExpenceClaimApproval. You can browse the code through the Code Library in an extension Library under the folder App_Data\CodeRepository\PX.Objects\EP\ExpenseClaimEntry.cs. The derived type is declared in the Descriptor\Attribute.cs file. There is a StatusHandler you can set that is executed upon approval/denial.
I built this test graph and was able to break in the middle of the approval process:
public class ExpenseClaimEntry_Extension : PXGraphExtension<ExpenseClaimEntry>
{
public void CustomApprovalAction(EPExpenseClaim item)
{
//do something
}
public override void Initialize()
{
base.Initialize();
Base.Approval.StatusHandler += CustomApprovalAction;
}
}

How to extend OrchardCMS to show/hide navigation and content from intranet vs internet users

I have certain pages that I want accessible only to users that are accessing the site from within a given IP range. For all other users, these pages should be inaccessible, and their respective links not visible in the menu/navigation.
I'm new to OrchardCMS, can someone provide some general guidance and point me in the right direction?
There two aspects to answer your question.
1. To check access to orchard content items and menu item relative to it:
To achieve this, you can implement new IAuthorizationServiceEventHandler to replace the default roles based authorization service, the best sample for you is ContentMenuItemAuthorizationEventHandler which you can find under Orchard.ContentPicker module, I included a sample code to explain the usage of this handler:
public class CustomAuthorizationEventHandler :
IAuthorizationServiceEventHandler{
public ContentMenuItemAuthorizationEventHandler() {
}
public void Checking(CheckAccessContext context) { }
public void Adjust(CheckAccessContext context) {
//Here you can put your business to grant user or not
context.Granted = true; //Roles service will look to this value to grant access to the user
context.Adjusted = true;
}
public void Complete(CheckAccessContext context) {}
}
2. To check access to some actions.
To achieve this, you can implement new IAuthorizationFilter to check access to some actions in your system:
public class CustomAuthorizationFilter : FilterProvider, IAuthorizationFilter {
public void OnAuthorization(AuthorizationContext filterContext) {
if (!Granted) {
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
The solutions mentioned by #mdameer are ok, but you will run into difficulties when using containers, lists, projections and stuff.
I had a similar task but with date time ranges. See my question and answer to the task to get an idea how to tackle this via a custom part:
How to skip displaying a content item in Orchard CMS?

How can I execute code from the Release / Release All buttons in the Release AR Documents screen

I've got a customization to the Invoice & Memo screen where I execute some custom code (web service calls) when the Release action is activated. This works fine - I knew how to replace the PXAction code and proceeded from there. Now I want to use the Release AR Documents processing screen to do the same thing, but I'm having trouble understanding where / what to override, or where to place my code.
I see the ARDocumentRelease graph constructor with the SetProcessDelegate in the source code, but I'm not sure how to proceed - whether this is where I need to be looking or not. I need to execute my code for each line being released, using the RefNbr in my code.
Since it's an static method, you can't override it. Also, you can't do like it's done in the T300, because you are in processing graph and you can't override the release button with your own. I was able to achieve it by passing callback for each AR document that have been processed.
You can call the Initialize method of the ARDocumentRelease graph to override the logic like you said. After you just have to call ReleaseDoc that uses a callback parameter instead of using the default one.
Here's the code that I came with:
public class ARDocumentRelease_Extension : PXGraphExtension<ARDocumentRelease>
{
public override void Initialize()
{
ARSetup setup = Base.arsetup.Current;
Base.ARDocumentList.SetProcessDelegate(
delegate (List<BalancedARDocument> list)
{
List<ARRegister> newlist = new List<ARRegister>(list.Count);
foreach (BalancedARDocument doc in list)
{
newlist.Add(doc);
}
AddAdditionalLogicToRelease(newlist);
}
);
Base.ARDocumentList.SetProcessCaption("Release");
Base.ARDocumentList.SetProcessAllCaption("Release All");
}
public delegate void PostPorcessing(ARRegister ardoc, bool isAborted);
private void AddAdditionalLogicToRelease(List<ARRegister> newlist)
{
ARDocumentRelease.ReleaseDoc(newlist, true, null, delegate(ARRegister ardoc, bool isAborted) {
//Add your logic to handle each document
//Test to check if it was not aborted
});
}
}
Please note that you must always call static methods from within long running process and create necessary objects there.
Processing delegate logic is implemented as long running process which creates worker thread to execute the processing logic.
You have AddAdditionalLogicToRelease() method which requires object instance in order to call and will fail during thread context switches and hence the issue. So, you must have create object instance inside the thread context and then call instance method.
In general, method that gets called from long running processes are declared static and required objects/graphs are created inside this static method to do some work. See below example how to properly override ARDocumentRelease graph for this purpose:
public class ARDocumentRelease_Extension : PXGraphExtension<ARDocumentRelease>
{
public override void Initialize()
{
Base.ARDocumentList.SetProcessDelegate(
delegate (List<BalancedARDocument> list)
{
List<ARRegister> newlist = new List<ARRegister>(list.Count);
foreach (BalancedARDocument doc in list)
{
newlist.Add(doc);
}
// use override that allows to specify onsuccess routine
ARDocumentRelease.ReleaseDoc(newlist, true, null, (ardoc, isAborted) =>
{
//Custom code here, such as create your GL
});
}
);
}
}
I think it's the function
public static void ReleaseDoc(List<ARRegister> list, bool isMassProcess, List<Batch> externalPostList, ARMassProcessDelegate onsuccess)
under ARDocumentRelease businesss logic.

c# MVC Site Map - very slow when using roles - very slow

I've installed MVC Site Map Provider for MVC5 and just used everything out of the the box. It works fine. Now I want to implement roles based menu trimming so assuming my controller:
public class Home: Controller
{
[Authorize(Roles="Admin")]
public ActionResult Index()
{
return View();
}
}
Now basically only Admin role users can see the menu. Perfect works fine.
Also to implement this I added to my web.config this line:
<add key="MvcSiteMapProvider_SecurityTrimmingEnabled" value="true" />
The problem is that it works but it's slow. It takes about 7 seconds for the page to load. If I remove the web.config line, basically removing menu trimming based on roles it takes ~300ms for the page to load. Something is wrong in here.
Any ideas why my menu trimming based on roles is slow? I haven't done any customizations.
The security trimming feature relies on creating a controller instance for every node in order to determine if the current user context has access.
The most likely cause of this slowness is that your controllers (or their base class) have too much heavy processing happening in the constructor.
public class HomeController
{
public HomeController() {
// Lots of heavy processing
System.Threading.Thread.Sleep(300);
};
}
The above example will add 300 ms to the page load time for every node that represents an action method in the HomeController. If your other controllers also have heavy processing during instantiation, they will also add additional time to each page load.
When following DI best practices, this is not an issue because heavy processing takes place in external services after the controller instance is created.
public interface IHeavyProcessingService
{
IProcessingResult DoSomethingExpensive();
}
public class HeavyProcessingService : IHeavyProcessingService
{
public HeavyProcessingService() {
}
public IProcessingResult DoSomethingExpensive() {
// Lots of heavy processing
System.Threading.Thread.Sleep(300);
}
}
public class HomeController
{
private readonly IHeavyProcessingService heavyProcessingService;
// The constructor does no heavy processing. It is deferred until after
// the instance is created by HeavyProcessingService.
// The only thing happening here is assignment of dependencies.
public HomeController(IHeavyProcessingService heavyProcessingService) {
if (heavyProcessingService == null)
throw new ArgumentNullException("heavyProcessingService");
this.heavyProcessingService = heavyProcessingService;
};
public ActionResult Index()
{
var result = this.heavyProcessingService.DoSomethingExpensive();
// Do something with the result of the heavy processing
return View();
}
public ActionResult About()
{
return View();
}
public ActionResult Contact()
{
return View();
}
}
Notice in the above example that no heavy processing happens in the constructor? This means that creating an instance of HomeController is very cheap. It also means that action methods that don't require the heavy processing to happen (as in About() and Contact() in the example) won't take the hit of heavy processing required by Index().
If not using DI, MVC still requires that a new controller instance be created for each request (controller instances are never shared between users or action methods). Although, in that case it is not as noticeable on a per user basis because only 1 instance is created per user. Basically, MvcSiteMapProvider is slowing down because of a pre-existing issue with your application (which you can now fix).
Even if you are not using DI, it is still a best practice to defer heavy processing until after the controller instance is created.
public class HomeController
{
private readonly IHeavyProcessingService heavyProcessingService;
public HomeController() {
this.heavyProcessingService = new HeavyProcessingService();
};
public ActionResult Index()
{
var result = this.heavyProcessingService.DoSomethingExpensive();
// Do something with the result of the heavy processing
return View();
}
}
But if moving heavy processing into external services in your application is not an option, you can still defer processing until its needed by moving the processing into another method so it is not too expensive to create controller instances.
public class HomeController
{
public HomeController() {
};
private IProcessingResult DoSomethingExpensive() {
// Lots of heavy processing
System.Threading.Thread.Sleep(300);
}
public ActionResult Index()
{
var result = this.DoSomethingExpensive();
// Do something with the result of the heavy processing
return View();
}
}
Although there is a bug posted for Route values not preserved correctly in v4?
But looks like it was fixed in version 4 next release.
Another Workaround to fix this problem is cache here is a related article.
MVC siteMap provider cache

Access Orchard Content Part Buttons (Save and Publish Now)

I want to disable Orchard Content Part buttons (Save and Publish Now) in the EDITOR template (when Content Item is created) based on some conditions. Can I do that ? How do I access the buttons in the EDITOR view.
Here are come examples,
To build a content fully from a Controller example, taken from the Blog Module
public ActionResult Create() {
if (!Services.Authorizer.Authorize(Permissions.ManageBlogs, T("Not allowed to create blogs")))
return new HttpUnauthorizedResult();
BlogPart blog = Services.ContentManager.New<BlogPart>("Blog");
if (blog == null)
return HttpNotFound();
dynamic model = Services.ContentManager.BuildEditor(blog);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
[HttpPost, ActionName("Create")]
public ActionResult CreatePOST() {
if (!Services.Authorizer.Authorize(Permissions.ManageBlogs, T("Couldn't create blog")))
return new HttpUnauthorizedResult();
var blog = Services.ContentManager.New<BlogPart>("Blog");
_contentManager.Create(blog, VersionOptions.Draft);
dynamic model = _contentManager.UpdateEditor(blog, this);
if (!ModelState.IsValid) {
_transactionManager.Cancel();
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
_contentManager.Publish(blog.ContentItem);
return Redirect(Url.BlogForAdmin(blog));
}
BuidEditor does the work for you.
And you should use a alternative version of this template, but remove the edit link and publish link.
Note, you need a route for you custom create action, and a menu link on the dashboard may come in handy.

Resources