I have DrawingBean as
public class DrawingBean {
String drawingTitle;
public String getDrawingTitle() {
return drawingTitle;
}
public void setDrawingTitle(String drawingTitle) {
this.drawingTitle = drawingTitle;
}
}
In JSF I have page drawingPatentss.xhtml which contains below within h:form tag.
<h:inputText value="#{PersonalInformationDataBean.drawingTitle}" size="80" id="drawingTitle" />
<h:commandButton value="Upload More Drawing" action="# {PersonalInformationDataBean.checkMyDrawingPageData()}" />
<h:dataTable id="patentDrawingList" var="patentDrawingList" value="#{PersonalInformationDataBean.drawingList}" border="1" width="30%">
<h:column>
<f:facet name="header">
<h:outputText value="Drawing Title" />
</f:facet>
<h:outputText value="#{patentDrawingList.drawingTitle}"/>
<h:commandButton value="Delete" action="#{PersonalInformationDataBean.removeDataFromDrawingList()}">
<f:setPropertyActionListener target="#{PersonalInformationDataBean.drawingTitle}" value="#{patentDrawingList.drawingTitle}" />
</h:commandButton>
</h:column>
</h:dataTable>
In PersonalInformationDataBean.java I have
private List<DrawingBean> drawingList = new ArrayList<DrawingBean>();
private String drawingTitle;
DrawingBean drawingBean;
// getter and setter for all of above.
public String checkMyDrawingPageData() {
drawingBean = new DrawingBean();
drawingBean.setDrawingTitle(drawingTitle);
drawingList.add(drawingBean);
return "drawingPatentss?faces-redirect=true";
}
public String removeDataFromDrawingList() {
drawingBean = new DrawingBean();
drawingBean.setDrawingTitle(drawingTitle);
boolean removedStatus = false;
removedStatus = drawingList.remove(drawingBean);
System.out.println("removed status === " + removedStatus);
return "drawingPatentss?faces-redirect=true";
}
However the bean is not getting removed from the list...
However if I add below lines in removeDataFromDrawingList(), the list gets updated.
drawingBean = new DrawingBean();
drawingBean.setDrawingTitle("Temp Title");
drawingList.add(drawingBean);
Any reason why the request bean is not getting removed from the list that I have.
Any help would really appreciated.
I got solution. I had to use Iterator class. Below code did the trick...
drawingBean = new DrawingBean();
drawingBean.setDrawingTitle(drawingTitle);
boolean removedStatus = false;
for (Iterator itr=drawingList.iterator();itr.hasNext();) {
drawingBean = (DrawingBean) itr.next();
if (drawingBean.getDrawingTitle().equals(drawingTitle)) {
itr.remove();
removedStatus = true;
}
}
Reference
Related
I want to be able to perform an asynchronous task in java and be able to keep a completion (and if possible progress) monitor associated to the user's session. Is this possible, and if yes what is the way to do it?
Currently the task is implemented synchronously as a stateless session bean method, which is called from a jax-rs endpoint.
I looked at https://docs.oracle.com/javaee/7/tutorial/ejb-async001.htm but AsyncResult is not serializable so I guess I cannot add it to session.
Using the Spring annotation #Async, you can make any bean/method asynchronous.
The container will create a new thread and method will be executed asynchronously. You can as well pass a session object into this method and upon completion, you can mark an attribute in the session object.
Example:- https://spring.io/guides/gs/async-method/
JSF example, works in Wildfly:
1 inside in view (xhtml) we have an upload form and progress meter
<h:form>
<div align="justify">
<p:fileUpload style="width: auto" fileUploadListener="#{fileUploadCtrl.handleFileUpload}" mode="advanced" label="Please pick XLS file" update="messages" auto="true" sizeLimit="1000000" allowTypes="/(\.|\/)(xls|xlsx)$/" />
<p:growl id="messages" showDetail="false" life="4000"/>
</div>
</h:form>
<h:form id="wholeform">
<h:outputText id="statusot" value="#{fileUploadCtrl.message}" />
<p:spacer width="10" height="10"/>
<p:poll interval="1" listener="#{fileUploadCtrl.updateStatus}" update="wholeform" />
</h:form>
2 in controller, which is a managed bean, we process file and once a second update status
#ManagedBean
#ViewScoped
public class FileUploadCtrl {
#EJB
private SomeBusinessLogicClass model;
#EJB
private ProgressTracker progress;
private Future<List<String>> asyncResult;
private int progressId = 0;
private String message;
private boolean busy = false;
public void handleFileUpload(FileUploadEvent event) {
Set<String> ids = model.populate(event.getFile().getContents());
progressId = progress.newIndex();
asyncResult = model.process(ids);
busy = true;
FacesMessage message = new FacesMessage("Loaded " + ids.size() + " objects", "");
FacesContext.getCurrentInstance().addMessage(null, message);
}
public void updateStatus() {
if (!busy)
return;
try {
if (asyncResult.isDone()) {
List<String> r = asyncResult.get();
message = "Job done";
busy = false;
progress.delIndex(progressId);
} else {
message = progress.getIndex(progressId)+"-th element in work";
}
} catch (Exception e) {
System.out.println("updateStatus " + e.toString());
}
}
3 All business logic is in EJBs like SomeBusinessLogicClass or many others. Also we need a simple progress-manager EJB, I post it completely
#Singleton
public class ProgressTracker {
private Map<Integer,Integer> indexes = new HashMap<>();
public Map<Integer, Integer> getIndexes() {
return indexes;
}
public void setIndexes(Map<Integer, Integer> indexes) {
this.indexes = indexes;
}
public Integer newIndex() {
Integer size = indexes.size();
indexes.put(size,0);
return size;
}
public void incIndex(final Integer index) {
int old = indexes.get(index);
old++;
indexes.put(index,old);
}
public Integer getIndex(final Integer index) {
return indexes.get(index);
}
public void delIndex(Integer index) {
indexes.remove(index);
}
}
Maybe this example is not elegant, I'm almost newbie with frontends, but it is working and better, than nothing.
I'm trying to follow the DevExpress documentation example on cloning rows. The basics are easy to get right, but things get tricky when you try to clone properties other than value types.
First attempt / setting up a repro:
I've narrowed my real scenario to a small(ish) repro of the problem. The following is all part of a freshly created ASP.NET 2.0 webforms application. First, suppose these domain objects (that'll double as DTO's here too):
public class Qualification
{
public long Id { get; set; }
public string Title { get; set; }
}
public class Person
{
public long Id { get; set; }
public string Name { get; set; }
public Qualification Qualification { get; set; }
}
Then there are two quick 'n dirty data source classes like so:
public class QualificationOds
{
public static List<Qualification> Qualificiations = new List<Qualification>
{
new Qualification { Id = 1, Title = "Doctore" }
};
public List<Qualification> GetRecords()
{
return Qualificiations;
}
}
public class PeopleOds
{
public List<Person> GetRecords()
{
return new List<Person>
{
new Person
{
Id = 1,
Name = "John Doe",
Qualification = QualificationOds.Qualificiations[0]
}
};
}
}
The default.aspx page will first register dx to DevExpress namespaces:
<%# Register Assembly="DevExpress.Web.ASPxGridView.v11.1, Version=11.1.11.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web.ASPxGridView" TagPrefix="dx" %>
<%# Register Assembly="DevExpress.Web.ASPxEditors.v11.1, Version=11.1.11.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web.ASPxEditors" TagPrefix="dx" %>
And have a single form with this code in it:
<asp:ObjectDataSource runat="server" ID="peopleDataSource" TypeName="DevxTest.PeopleOds" SelectMethod="GetRecords" />
<asp:ObjectDataSource runat="server" ID="qualificationsDataSource" TypeName="DevxTest.QualificationOds" SelectMethod="GetRecords" />
<dx:ASPxGridView
runat="server"
id="grvPeople"
KeyFieldName="Id"
DataSourceID="peopleDataSource"
OnCustomButtonCallback="grid_CustomButtonCallback"
OnInitNewRow="grid_InitNewRow">
<Columns>
<dx:GridViewCommandColumn>
<EditButton Visible="true" />
<CustomButtons>
<dx:GridViewCommandColumnCustomButton ID="Clone" Text="Clone" />
</CustomButtons>
</dx:GridViewCommandColumn>
<dx:GridViewDataTextColumn FieldName="Name" />
<dx:GridViewDataComboBoxColumn FieldName="Qualification.Id" >
<PropertiesComboBox
DataSourceID="qualificationsDataSource"
TextField="Title"
ValueField="Id"
ValueType="System.Int64" />
</dx:GridViewDataComboBoxColumn>
</Columns>
</dx:ASPxGridView>
The code behind contains the code to clone a row into a fresh edit form, based on aforementioned DevExpress example, like so:
private string[] fieldsToCopy = { "Name", "Id" };
private Hashtable valuesToClone;
protected void grid_CustomButtonCallback(object sender, ASPxGridViewCustomButtonCallbackEventArgs e)
{
if (e.ButtonID != "Clone") return;
valuesToClone = new Hashtable();
foreach (var fieldName in fieldsToCopy)
{
valuesToClone[fieldName] = grvPeople.GetRowValues(e.VisibleIndex, fieldName);
}
grvPeople.AddNewRow();
}
protected void grid_InitNewRow(object sender, DevExpress.Web.Data.ASPxDataInitNewRowEventArgs e)
{
if (valuesToClone == null) return;
foreach (string fieldName in fieldsToCopy)
{
e.NewValues[fieldName] = valuesToClone[fieldName];
}
}
This works for the Name property, but not so much for the Qualification property. It shows like this on the page:
I've also tried "Qualification.Id" and "Qualification" as field names to copy, but no dice. At any rate, I'm pretty sure it should be "Id" as in my example though, because the debugger shows the correct GetRowValues return value only for that case.
Second attempt / custom code to handle the dropdown:
So I'm thinking I need to have some custom code to set the NewValue for a dropdown. First I create an EditItemTemplate for the Qualification column like so:
<EditItemTemplate>
<dx:ASPxComboBox
runat="server"
ID="qualificationCombo"
DataSourceID="qualificationsDataSource"
TextField="Title"
ValueField="Id"
ValueType="System.Int64" />
</EditItemTemplate>
And then I add this code to grid_InitNewRow:
// Attempt to set the combo box to a value:
var column = grvPeople.Columns["Qualification"] as GridViewDataColumn;
var comboBox = grvPeople.FindEditRowCellTemplateControl(column, "qualificationCombo") as ASPxComboBox;
var item = comboBox.Items.FindByValue(valuesToClone["Id"]);
item.Selected = true;
And it works! However, now the Name is no longer cloned. It looks like this:
I have no clue why, but the added code has a side-effect causing the NewValues setter approach to fail.
Bottom Line:
The bottom line is rather simple: how do I change the DevExpress example into one that can also clone reference type properties?
PS. I've also cross-posted my question on the DevExpress support forum.
You mention that "Qualification.Id" doesn't work, but you most likely tried that while the EditItemTemplate was there. If there is no such template then that code actually works.
To be complete, change the fieldsToCopy line of code to:
private string[] fieldsToCopy = { "Name", "Qualification.Id" };
And make sure there's no EditItemTemplate, then it'll work.
Alternatively, if you are stuck with a EditItemTemplate for some reason, there's one "solution" to make sure the ComboBox in that template gets bound to the cloned value. Make sure you hook into OnDataBound on the ComboBox with the following code:
protected void qualificationCombo_DataBound(object sender, EventArgs e)
{
var column = grvPeople.Columns["Qualification"] as GridViewDataColumn;
var comboBox = grvPeople.FindEditRowCellTemplateControl(column, "qualificationCombo") as ASPxComboBox;
var item = comboBox.Items.FindByValue(valuesToClone["Id"]);
item.Selected = true;
}
This will properly set the ComboBox to the cloned value, while also leaving the e.NewValues approach for plain fields intact.
I am in the middle of upgrading from umbraco v6.0.5 to v7.2.1.
In my current(v6) web.config I have the following:
<profile defaultProvider="UmbracoMemberProfileProvider" enabled="true" inherits="NFOPP.Website.usercontrols.Security.MemberProfile, NFOPP.Website">
<providers>
<clear />
<add name="UmbracoMemberProfileProvider" type="umbraco.providers.members.UmbracoProfileProvider, umbraco.providers" />
</providers>
<properties>
<clear />
<add name="_title" allowAnonymous="false" provider="UmbracoMemberProfileProvider" type="System.String" />
<add name="_firstname" allowAnonymous="false" provider="UmbracoMemberProfileProvider" type="System.String" />
<add name="_lastname" allowAnonymous="false" provider="UmbracoMemberProfileProvider" type="System.String" />
<add name="_memberNumber" allowAnonymous="false" provider="UmbracoMemberProfileProvider" type="System.String" />
</properties>
</profile>
I also have a class like this:
public class MemberProfile : ProfileBase
{
#region Firstname
private const string FIRSTNAME = "_firstname";
[SettingsAllowAnonymous(false)]
public string FirstName
{
get
{
return GetCustomProperty(FIRSTNAME);
}
set
{
SetCustomProperty(FIRSTNAME, value);
}
}
#endregion
#region Get and Set base properties
private string GetCustomProperty(string propertyName)
{
var retVal = "";
var prop = base.GetPropertyValue(propertyName);
if (prop != null)
{
retVal = prop.ToString();
}
return retVal;
}
private void SetCustomProperty(string propertyName, object value)
{
var prop = base[propertyName];
if (prop != null)
{
base.SetPropertyValue(propertyName, value);
}
}
#endregion
}
The purpose of this is so I can work with my MemberProfile object and access it's properties like FirstName directly.
The "umbraco.providers.members.UmbracoProfileProvider" does not seem to exist (unless I am mistaken) any more meaning I cannot seem to wire up a class that overrides the "ProfileBase" properly, and when I attempt to load a page that accesses the "MemberProfile" object it says that "No PropertyType exists with the supplied alias _firstname"
Is there a better way to do this, and in v7 is the config section actually needed any more if there is no provider?
Solution Implemented:
I added the following methods to my Member Profile class:
public static MemberProfile Get(IMemberService memberService, UmbracoContext context)
{
var membershipHelper = new Umbraco.Web.Security.MembershipHelper(context);
var profileModel = membershipHelper.GetCurrentMemberProfileModel();
if (profileModel != null)
{
return Get(profileModel.UserName, memberService);
}
else
{
return null;
}
}
public static MemberProfile Get(string userName, IMemberService memberService)
{
var member = memberService.GetByUsername(userName);
return new MemberProfile(member);
}
I also updated the Get and Set property methods like so:
#region Get and Set base properties
private string GetCustomProperty(string propertyName)
{
var retVal = "";
if (_member.Properties.IndexOfKey(propertyName) > -1)
{
var prop = _member.Properties[propertyName];
if (prop != null && prop.Value != null)
{
retVal = prop.Value.ToString();
}
}
return retVal;
}
private void SetCustomProperty(string propertyName, object value)
{
if (_member.Properties.IndexOfKey(propertyName) > -1)
{
var prop = _member.Properties[propertyName];
if (prop != null)
{
prop.Value = value;
}
}
}
#endregion
To use this I then called the following wherever I needed the member object:
var member = MemberProfile.Get(ApplicationContext.Current.Services.MemberService, Umbraco.Web.UmbracoContext.Current);
I would still appreciate any thoughts around this solution and how other people handle adding custom properties to the member and accessing them from code.
It's by heart, but I thought you still have the asp.net based profile provider like this:
<profile defaultProvider="UmbracoMemberProfileProvider" enabled="true" inherits= "Umbarco.Models.MemberProfile, Umbarco">
Consider using the new Membership API introduced in v7.1
Issues with custom workflow activity in CRM 2013 On-prem
I'm trying to pass the Manager of the System
here is the code that I'm running, it gets to setting the MANAGER and stops
I put the ran the FetchXML seperatly and it does return a value so I know what bit works
public class CaseAccountManagerManagersLookup : CodeActivity
{
// Inputs
[Input("Enter Case")]
[ReferenceTarget("incident")]
public InArgument<EntityReference> CA { get; set; }
// Outputs
[Output("Manager Output")]
[ReferenceTarget("systemuser")]
public OutArgument<EntityReference> AMOUT { get; set; }
protected override void Execute(CodeActivityContext executionContext)
{
// Context
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
//Create the tracing service
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
// get the account and renewals manager ID's
var CASE = CA.Get<EntityReference>(executionContext);
tracingService.Trace("Case ID = " + CASE.Id);
try
{
// FETCH
string fetchXml = string.Format(#"
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='incident'>
<attribute name='title' />
<attribute name='incidentid' />
<order attribute='title' descending='false' />
<filter type='and'>
<condition attribute='incidentid' operator='eq' value='{0}' />
</filter>
<link-entity name='contact' from='contactid' to='customerid' alias='ak'>
<link-entity name='account' from='accountid' to='parentcustomerid' alias='al'>
<link-entity name='systemuser' from='systemuserid' to='bc_dssalesperson' alias='am'>
<attribute name='parentsystemuserid' />
</link-entity>
</link-entity>
</link-entity>
</entity>
</fetch>", CASE.Id);
EntityCollection case_results = service.RetrieveMultiple(new FetchExpression(fetchXml));
//tracingService.Trace("fetch has run");
if (case_results.Entities.Count != 0)
{
foreach (var a in case_results.Entities)
{
//if (a.Attributes.Contains("ai_parentsystemuserid"))
//{
tracingService.Trace("set manager id next");
var MANAGERID = (EntityReference)a.Attributes["parentsystemuserid"];
tracingService.Trace("manager id set");
AMOUT.Set(executionContext, MANAGERID);
throw new InvalidOperationException("Want to see trace");
//}
}
}
tracingService.Trace("end ");
}
catch (Exception e)
{
throw new InvalidPluginExecutionException("Plugin - CaseAccountManagerManagerLookup - " + e.Message);
}
finally
{
throw new InvalidOperationException("Want to see trace");
}
}
}
Try to use am.parentsystemuserid instead of just parentsystemuserid.
Are you sure that the guid that you are passing is in the correct form?
{8B8099A6-8B89-E411-883D-D89D676552A0}
this is what i get from the export but you are writing
8B8099A6-8B89-E411-883D-D89D676552A0
Also are you sure about the record that you are trying to retrieve? Is the chain complete with data?
case-> contact -> account -> parent user -> parent user ?
I was asked to develop an auto-mail sending program on asp.net. It is supposed to send, say 5000 e-mails reading the addresses from a database. It will sure fall into request timeout tough. So it seems I have to convert it into a windows app. But I'd like to know if ajaxifying this web-app would help. If I write a web service, and my web app sends the mail addresses as lists of 50 at a time. when done, send the next 50 and so on. Would this help to solve the problem of http request timeout?
Using a webservice endpoint to send your emails is a good idea, whether you call it from an aspx class or from the client with javascript.
Simply use the webservice call to spawn a thread to send the emails and return immediately.
If you wanted visual progress cues then write another ajax endpoint or aspx page that will display the status of the email thread's progress.
There are many ways to accomplish this, you should be able to come up with one with the information given.
Batching from ajax is probably going to be more work than you want to do and adds unnecessary complexity (which is never a good thing).
this is interesting. I may spike this and post some code.
Ok, im back. here ya go. both a webform ui and an ajax ui.
None of this is meant to be finished product - is a spike to support a story. bend/fold/spindle at will.
EmailService.asmx
using System;
using System.ComponentModel;
using System.Threading;
using System.Web.Script.Services;
using System.Web.Services;
namespace EmailSendingWebApplication
{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class EmailService : WebService
{
private static EmailSendingProgress _currentProgress;
private static Thread _emailThread;
/// <summary>
///
/// </summary>
/// <param name="criteria">just an example</param>
/// <param name="anotherCriteria">just an example</param>
/// <returns></returns>
[WebMethod]
public EmailSendingProgress SendEmails(string criteria, int anotherCriteria)
{
try
{
if (_currentProgress != null && _emailThread.IsAlive)
{
throw new InvalidOperationException(
"Email batch is already in progress. Wait for completion or cancel");
}
// use your criteria to cue up the emails to be sent.
// .....
// and derive a way for a thread to identify the emails
// i am using a batchid
int batchId = 1000; // contrived
// create a thread
_emailThread = new Thread(ProcessEmails);
_currentProgress = new EmailSendingProgress
{
Status = ProcessState.Starting,
BatchId = batchId
};
// you could use a 'state' object but this process/thread
// is single use/single instance just access _currentProgress
// by the static member
_emailThread.Start();
return _currentProgress;
}
catch (Exception ex)
{
_currentProgress = new EmailSendingProgress
{
Status = ProcessState.Error,
Message = "Error starting process:" + ex.Message
};
}
return _currentProgress;
}
[WebMethod]
public EmailSendingProgress CancelEmailProcess()
{
if (_currentProgress != null && _emailThread.IsAlive)
{
_currentProgress.Cancel = true;
_currentProgress.Message = "Cancelling";
}
return _currentProgress;
}
[WebMethod]
public EmailSendingProgress GetProgress()
{
return _currentProgress;
}
private static void ProcessEmails()
{
// process your emails using the criteria, in this case,
// a batchId
int totalEmails = 100;
int currentEmail = 0;
lock (_currentProgress)
{
_currentProgress.Total = totalEmails;
_currentProgress.Status = ProcessState.Processing;
}
for (; currentEmail < totalEmails; currentEmail++)
{
lock (_currentProgress)
{
if (_currentProgress.Cancel)
{
_currentProgress.Status = ProcessState.Cancelled;
_currentProgress.Message = "User cancelled process.";
break;
}
_currentProgress.Current = currentEmail + 1;
}
try
{
// send your email
Thread.Sleep(100); // lallalala sending email
}
catch (Exception ex)
{
// log the failure in your db
// then check to see if we should exit on error
// or just keep going.
lock (_currentProgress)
{
if (_currentProgress.CancelBatchOnSendError)
{
_currentProgress.Status = ProcessState.Error;
_currentProgress.Message = ex.Message;
break;
}
}
}
}
{
// don't want to obscure state/message from abnormal
// termination..
if (_currentProgress.Status == ProcessState.Processing)
{
_currentProgress.Status = ProcessState.Idle;
_currentProgress.Message = "Processing complete.";
}
}
}
}
public enum ProcessState
{
Idle,
Starting,
Processing,
Cancelled,
Error
}
[Serializable]
public class EmailSendingProgress
{
public int BatchId;
public bool Cancel;
public bool CancelBatchOnSendError;
public int Current;
public string Message;
public ProcessState Status;
public int Total;
}
}
WebFormUI.aspx
<%# Page Language="C#" %>
<%# Import Namespace="EmailSendingWebApplication" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
var svc = new EmailService();
UpdateProgress(svc.GetProgress());
}
protected void SendEmailsButton_Click(object sender, EventArgs e)
{
// arbitrary params - modify to suit
string criteria = string.Empty;
int anotherCriteria = 0;
var svc = new EmailService();
UpdateProgress(svc.SendEmails(criteria, anotherCriteria));
}
protected void CancelEmailProcessButton_Click(object sender, EventArgs e)
{
var svc = new EmailService();
UpdateProgress(svc.CancelEmailProcess());
}
private void UpdateProgress(EmailSendingProgress progress)
{
SetButtonState(progress);
DisplayProgress(progress);
}
private void DisplayProgress(EmailSendingProgress progress)
{
if (progress != null)
{
EmailProcessProgressLabel.Text = string.Format("Sending {0} of {1}", progress.Current, progress.Total);
EmailProcessStatusLabel.Text = progress.Status.ToString();
EmailProcessMessageLabel.Text = progress.Message;
}
else
{
EmailProcessProgressLabel.Text = string.Empty;
EmailProcessStatusLabel.Text = string.Empty;
EmailProcessMessageLabel.Text = string.Empty;
}
}
private void SetButtonState(EmailSendingProgress progress)
{
if (progress != null &&
(progress.Status == ProcessState.Starting || progress.Status == ProcessState.Processing))
{
CancelEmailProcessButton.Visible = true;
SendEmailsButton.Visible = false;
}
else
{
CancelEmailProcessButton.Visible = false;
SendEmailsButton.Visible = true;
}
}
protected void RefreshButton_Click(object sender, EventArgs e)
{
// noop just to get postback. you could also use meta headers to refresh the page automatically
// but why?
}
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<br />
EmailProcessStatus:
<asp:Label ID="EmailProcessStatusLabel" runat="server" Text="EmailProcessStatus"></asp:Label>
<br />
EmailProcessProgress:
<asp:Label ID="EmailProcessProgressLabel" runat="server" Text="EmailProcessProgress"></asp:Label>
<br />
EmailProcessMessage:<asp:Label ID="EmailProcessMessageLabel" runat="server" Text="EmailProcessMessage"></asp:Label>
<br />
<br />
<asp:Button ID="SendEmailsButton" runat="server" OnClick="SendEmailsButton_Click"
Text="Send Emails" />
<asp:Button ID="CancelEmailProcessButton" runat="server" OnClick="CancelEmailProcessButton_Click"
Text="Cancel Email Process" />
<br />
<br />
<asp:Button ID="RefreshButton" runat="server" OnClick="RefreshButton_Click" Text="Refresh" />
</div>
</form>
</body>
</html>
AjaxUI.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
//http://www.codeproject.com/Articles/38999/Consuming-ASP-net-WebServices-WCF-Services-and-sta.aspx
var ProcessState = ["Idle", "Starting", "Processing", "Cancelled", "Error"];
function createXHR() {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
else {
throw new Error("Could not create XMLHttpRequest object.");
}
return xhr;
}
function emailAjax(operation, postData, callback) {
var xhr = createXHR();
xhr.open("POST", "EmailService.asmx/" + operation, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
callback(xhr.responseText);
}
};
xhr.setRequestHeader("content-type", "application/json");
xhr.send(postData);
}
function $(id) {
var e = document.getElementById(id);
return e;
}
function startProcess() {
var postData = '{"criteria" : "something", "anotherCriteria" : "1"}';
emailAjax("SendEmails", postData, displayProgress);
}
function cancelProcess() {
emailAjax("CancelEmailProcess", null, displayProgress);
}
function getProgress() {
emailAjax("GetProgress", null, displayProgress);
}
function displayProgress(json) {
eval('var result=' + json + '; var prg=result.d;');
$("EmailProcessMessage").innerHTML = "";
$("EmailProcessStatus").innerHTML = "";
$("EmailProcessProgress").innerHTML = "";
$("CancelEmailProcessButton").style.display = "none";
$("SendEmailsButton").style.display = "none";
if (prg) {
$("EmailProcessMessage").innerHTML = prg.Message;
$("EmailProcessStatus").innerHTML = ProcessState[prg.Status];
$("EmailProcessProgress").innerHTML = "Sending " + prg.Current + " of " + prg.Total;
}
if (prg && (prg.Status == 1 || prg.Status == 2)) {
$("SendEmailsButton").style.display = "none";
$("CancelEmailProcessButton").style.display = "inline";
}
else {
$("CancelEmailProcessButton").style.display = "none";
$("SendEmailsButton").style.display = "inline";
}
}
function init() {
$("SendEmailsButton").onclick = startProcess;
$("CancelEmailProcessButton").onclick = cancelProcess;
// kinda quick but we are only proccing 100 emails for demo
window.setInterval(getProgress, 1000);
}
</script>
</head>
<body onload="init()">
EmailProcessStatus:<span id="EmailProcessStatus"></span><br />
EmailProcessProgress:<span id="EmailProcessProgress"></span><br />
EmailProcessMessage:<span id="EmailProcessMessage"></span><br />
<input type="button" id="SendEmailsButton" value="SendEmails" style="display: none" />
<input type="button" id="CancelEmailProcessButton" value="CancelEmailProcess" style="display: none" />
</body>
</html>
So user will have to leave the browser window open until all the e-mails are sent? Does not sound very good. I would solve this using a daemon or simple script that is run by cron (and checks db if there is something to send), on Windows I hope you can do something similar (write Windows service etc.). This is a purely server-side task, I think ajaxifying it shows that author of the web app wasn't able to make it in a better way, it may even make your web app to be mentioned on thedailywtf.com :)