Acumatica: Sign Out User After Process Completion - acumatica

I am trying to sign out a user once a process is completed, I tried using the PXAccess or the PXAccessInfo classes in order to do so but did not manage to find a correct way in logging out a user. Are there any other means in signing out a user which I might have glossed over?

I adapted the standard SignOut code so it can be run from a graph extension instead of a Aspx.cs web page. It is equivalent to this SignOut menu item:
In this example I put the code in SOOrderEntry Initialize override so it signs out the current user as soon as you navigate to the SalesOrderEntry graph. You can put it in an Action event handler but I haven't tested it in a PXLongOperation context which runs in a separate thread context:
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
public override void Initialize()
{
System.Web.UI.Page page = System.Web.HttpContext.Current.Handler as System.Web.UI.Page;
if (page != null)
{
PX.Data.PXLogin.LogoutUser(PX.Data.PXAccess.GetUserName(), page.Session.SessionID);
PX.Common.PXContext.Session.SetString("UserLogin", string.Empty);
string absoluteLoginUrl = PX.Export.Authentication.AuthenticationManagerModule.Instance.SignOut();
page.Session.Abandon();
PX.Data.Auth.ExternalAuthHelper.SignOut(System.Web.HttpContext.Current, absoluteLoginUrl);
PX.Export.Authentication.FormsAuthenticationModule.
RedirectToLoginPage(PX.Data.Auth.ExternalAuthHelper.SILENT_LOGIN + "=None", true);
}
}
}

Related

Workflow extension that doesn't run

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().

Java+Cucumber: get the current tag/abverb being executed

I am trying to print the current step being executed in Cucumber. I am using a custom formatter to print the step definition. However, I also want to print the current adverb (Given, When, Then, And...) that is being executed. I might be missing something as well, is it possible in Cucumber? Here is my code:
Formatter:
public class MyCucumberFormatter implements ConcurrentEventListener {
#Override
public void setEventPublisher(EventPublisher publisher) {
publisher.registerHandlerFor(TestStepStarted.class, runStartedHandler);
}
private EventHandler<TestStepStarted> runStartedHandler = new EventHandler<TestStepStarted>() {
#Override
public void receive(TestStepStarted event) {
startReport(event);
}
};
private void startReport(TestStepStarted event) {
if (!(event.testStep instanceof PickleStepTestStep)) {
return;
}
PickleStepTestStep testStep = (PickleStepTestStep) event.testStep;
log("Step: " + testStep.getStepText());
}
}
Example scenario:
Scenario: Test user life cycle: create user, activate and delete
Given A valid admin logs in
When Admin creates new user
And User is activated
Then User should successfully login
Right now, it prints as:
A valid admin logs in
Admin creates new user
User is activated
User should successfully login
I want it to print as:
Given A valid admin logs in
When Admin creates new user
And User is activated
Then User should successfully login
You can't do this in v4.x yet but you can do this in v5.0.0-RC1 by going through pickleStepTestStep.getStep().getKeyWord()

Get user details in liferay layout listener

I'm creating a listener for liferay Layout model. I want to get the page creating/updating user details to the log. Here is a snippet from my code.
public class LayoutListener extends BaseModelListener<Layout> {
private final static Logger log = Logger.getLogger(LayoutListener.class);
#Override
public void onAfterRemove(Layout layout) throws ModelListenerException {
// Need to find user deatils here.
if (log.isInfoEnabled()) {
log.info("Page -- " + layout.getName() + " -- removed.");
}
super.onAfterRemove(layout);
}
}
How can I get the relevant user who is deleting the page inside this method?
PS - I was able to get user from accessing the current thread. But I need to know a proper way to do this.
Well here is how liferay fetches it for listeners in its Audit EE plugin:
if(PrincipalThreadLocal.getName() != null) {
userId = GetterUtil.getLong(PrincipalThreadLocal.getName());
}
And we are also using the same thing in our custom listeners for Blogs and Documents.

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.

Login control's loggedin event not firing

I've noticed that forums are littered with examples of this problem, but with the only solutions referring to events not actually being hooked up, or typos. I'm pretty sure that my problem relates to neither of these!
I have a login control with an opening tag that looks like this:
<asp:Login ID="signInControl" FailureText="Sorry, your login has not been successful" runat="server" DisplayRememberMe="false" OnLoggedIn="signInControl_LoggedIn" OnAuthenticate="signInControl_Authenticate" OnLoggingIn="signInControl_LoggingIn">
Here are my events in the code-behind:
protected void signInControl_LoggedIn(object sender, EventArgs e)
{
MembershipProvider provider = Membership.Providers[GlobalConstants.MembershipProviderName];
MembershipUser user = provider.GetUser(this.signInControl.UserName, true);
int expiryInDays = Utility.GetConfigurationValueAsInt(SPContext.Current.Web, "PasswordExpiryLengthInDays");
// provide a mechanism to stop password expiry (i.e. if -1);
if (expiryInDays > 0)
{
expiryInDays = expiryInDays * -1;
// If the user hasn't changed their password within the last x days, send them to update it.
DateTime minimumPasswordChange = DateTime.Now.AddDays(expiryInDays);
TimeSpan due = user.LastPasswordChangedDate.Subtract(minimumPasswordChange);
Logging.LogTrace(string.Format("The user {0} next password change is in {1} days.", user.UserName, due.Days));
if (user.LastPasswordChangedDate >= minimumPasswordChange)
{
SPUtility.Redirect("/_layouts/ExpiredPassword.aspx", SPRedirectFlags.Trusted, this.Context);
}
}
else
{
SPUtility.Redirect("/_layouts/ExpiredPassword.aspx", SPRedirectFlags.Trusted, this.Context);
}
}
protected void signInControl_Authenticate(object sender, AuthenticateEventArgs e)
{
SPClaimsUtility.AuthenticateFormsUser(new Uri(SPContext.Current.Web.Url), this.signInControl.UserName, this.signInControl.Password);
e.Authenticated = Membership.ValidateUser(this.signInControl.UserName, this.signInControl.Password);
}
protected void signInControl_LoggingIn(object sender, LoginCancelEventArgs e)
{
Logging.LogTrace(string.Format("User {0} attempting to sign in.", this.signInControl.UserName));
e.Cancel = false;
}
And as you can see in the authenticate event, e.authenticated is being set by whether the user can be validated. I've confirmed by stepping into the code that true is returned. So why isn't my signInControl_LoggedIn method firing? I'm seriously confused!
This is the markup for the front-end:
Thanks for any help!
Karl.
There are number of posts that describe how to create your own custom login page for SharePoint 2010. Here is the one I followed. In general, they create an application page that inherits from FormsSignInPage. For a page that just needs to change the look and feel, this works well. In some cases, like this one, creating the login page in this way may not be enough.
The way it is described at the end of this post, you need to inherit from IdentityModelSignInPageBase instead of FormsSignInPage. You also need to create an event handler for the authenticate event and register this in the OnLoad event of the page.
This is because the Authenticate method in the control will automatically take the user to the "ReturnUrl" page before it ever calls the OnLoggedIn event. It only takes two lines of code to authenticate the user in this method.
void signInControl_Authenticate(object sender, AuthenticateEventArgs e)
{
Login signInControl = sender as Login;
e.Authenticated = SPClaimsUtility.AuthenticateFormsUser(this.Page.Request.Url, signInControl.UserName, signInControl.Password);
}
Since your code now does not send the user to the ReturnUrl value, the OnLoggedIn event is reached.
You may ask why do I need the OnLoggedIn event if I have to create a custom Authenticate method. Well, there are still functions the control will handle for you. For instance, if the user does not authenticate (ie bad UN and PW combination), the control will display the error message and the OnLoggedIn never fires.
If you inherit from FormsSignInPage you may override your function RedirectToSuccessUrl:
protected override void RedirectToSuccessUrl()
{
//Your code here if successful login
base.RedirectToSuccessUrl();
}

Resources