Create your own (basic) DataEditor action
When you need to create an action with a simple form and render results based on this input, the easiest way to accomplish this is to create a custom class which inherits from the abstract class Smartsite.Manager.Actions.DataEditor.
Notice however, that this is a different implementation than creating a (custom) DataEditor action. In that case, the action you've created will (first) open a library containing the records for a specific table. After selecting a record and clicking the Edit button, then a new tab is opened which contains a DataEditor like implementation.
DataEditor
When directly inheriting from the abstract class Smartsite.Manager.Actions.DataEditor, you're creating an action which does use some of the same building blocks as a (custom) DataEditor action. You (also) need to load/create a DataEditor xml and you need to load/create an Instance document.
As example, the C# source code of a simplified version of the Google Pagespeed action is shown below.
| C# |
|
|---|---|
using Smartsite.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Smartsite.XForms;
namespace Smartsite.Manager.Actions
{
internal class GooglePageSpeed : DataEditor
{
protected override XDocument LoadDataEditor(MgrContext context)
{
string form = FormParser.LoadForm("Smartsite.Manager.Source.Actions.GooglePageSpeed.DataEditor.xml");
XDocument dataEditor = XDocument.Parse(form);
return dataEditor;
}
protected override XDocument LoadInstance(MgrContext context)
{
object obj;
string url = "";
if (context.Action.Parameters.TryGetValue("url", out obj) && obj != null)
url = (string)obj;
XDocument document = new XDocument();
XElement root = new XElement(XNamespaces.MetaDocument + "data",
new XElement(XNamespaces.MetaDocument + "entry",
new XElement(XNamespaces.MetaDocument + "url", url),
new XElement(XNamespaces.MetaDocument + "filterthirdparty", false),
new XElement(XNamespaces.MetaDocument + "screenshot", true),
new XElement(XNamespaces.MetaDocument + "strategy",
new XElement(XNamespaces.MetaDocument + "key", "desktop"))
));
document.Add(root);
return document;
}
public override ActionResult ButtonClicked(MgrContext context, XFormsSubmission submission, ref XDocument instance)
{
if (!submission.Id.Equals("save"))
return null;
XElement entry = instance.Root.Element(XNamespaces.MetaDocument + "entry");
string url = (string)entry.Element(XNamespaces.MetaDocument + "url");
url += "&screenshot=" + (string)entry.Element(XNamespaces.MetaDocument + "screenshot");
url += "&strategy=" + (string)entry.Element(XNamespaces.MetaDocument + "strategy");
url += "&filter_third_party_resources=" + (string)entry.Element(XNamespaces.MetaDocument + "filterthirdparty");
url += "&locale=" + UserWorkspace.Current.UserProfile.Culture;
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("url", url);
return ActionResult.StartSubAction(typeof(GooglePageSpeedResult), parameters);
}
}
}
|
|
The LoadDataEditor() function loads the DataEditor xml from an embedded resource, see example below.
| XML |
|
|---|---|
<dataeditor xmlns="http://smartsite.nl/namespaces/dataeditor/2.0" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events">
<layout title="GOOGLE_PAGESPEED">
<tabs>
<tab title="">
<field name="url" caption="URL">
<control type="textbox">
<properties>
<property name="maxlength">500</property>
<property name="required">true</property>
<property name="alert">{text:URL_REQUIRED}</property>
</properties>
</control>
</field>
<field name="filterthirdparty" caption="PAGESPEED_FILTER_THIRDPARTY">
<control type="checkbox">
<properties>
</properties>
</control>
</field>
<field name="screenshot" caption="PAGESPEED_INCLUDE_SCREENSHOT">
<control type="checkbox">
<properties>
</properties>
</control>
</field>
<field name="strategy" caption="PAGESPEED_STRATEGY">
<control type="radiobuttonlist">
<itemset ref="is_strategy" />
<properties>
</properties>
</control>
</field>
</tab>
</tabs>
<buttons>
<button code="save" css="icon_sys22 icon-sys22-execute" caption="EXECUTE" replace="instance" relevant="false"></button>
<button code="close"></button>
</buttons>
<modellogic xmlns:xf="http://www.w3.org/2002/xforms">
<xf:bind nodeset="cms:entry/cms:screenshot" type="xf:boolean" />
<xf:bind nodeset="cms:entry/cms:filterthirdparty" type="xf:boolean" />
</modellogic>
</layout>
<itemsets>
<itemset id="is_strategy" key="key" value="name">
<item key="desktop" value="{text:DESKTOP}" />
<item key="mobile" value="{text:MOBILE}" />
</itemset>
</itemsets>
</dataeditor>
|
|
This xml specifies the UI (the html input form).
The LoadInstance() function creates and returns the Instance xml. Since we're not editing an existing record for a specific table, we're able to construct this instance from scratch.
When the action is started, the following input form will be displayed:

The ButtonClicked() function is called when the Execute button is clicked (which in fact is the save button, but with a different caption).
This function creates an url, based on the user input, and starts a subaction of type GooglePageSpeedResult, passing this url as parameter.
GooglePageSpeedResult
For completeness' sake, the C# source code for the GooglePageSpeedResult class (a simplified version of it) is included below.
| C# |
|
|---|---|
using Smartsite.Base;
using Smartsite.Data;
using Smartsite.Manager.Resources;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web.Configuration;
namespace Smartsite.Manager.Actions
{
internal class GooglePageSpeedResult : BaseAction
{
public override ActionResult Execute(MgrContext context)
{
if (context.Request.Form["action"] == "close")
return ActionResult.CloseAction();
string url = null;
object obj;
string apiKey = WebConfigurationManager.AppSettings["google.pagespeed.apikey"];
if (String.IsNullOrWhiteSpace(apiKey))
apiKey = WebConfigurationManager.AppSettings["google.apikey"];
if (context.Action.Parameters.TryGetValue("url", out obj))
{
url = "https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=";
url += (string)obj;
if (!String.IsNullOrWhiteSpace(apiKey))
url += "&key=" + apiKey;
context.Action.State = url;
}
else
{
url = (string)context.Action.State;
}
if (url == null)
throw new Exception("Url parameter not specified");
ButtonBar buttonBar = new ButtonBar();
buttonBar.Buttons.Add(new Button("close", Translations.GetString("CLOSE"), "icon_sys22 icon-sys22-cancel", true));
context.Response.Layout[ThreeColumnLayout.ButtonBar] = buttonBar.Render();
StringBuilder sb = new StringBuilder();
context.Response.IncludedCss.Add(new CssInclude(ManagerResource.CreateUrl("css/library.css")));
context.Response.InlineJavascripts.Add("$(function () { shell.PrepareInterceptor($(\"form\")); });");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
string result = null;
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
}
}
catch (WebException we)
{
// code omitted
}
sb.AppendLine("<div class=\"selectable\">");
sb.AppendFormat("<h1>{0}</h1>", Translations.GetString("GOOGLE_PAGESPEED_RESULT"));
if (String.IsNullOrWhiteSpace(apiKey))
{
// show warning
sb.AppendFormat("<div><span class=\"information-message\">{0}</span></div>", Translations.GetString("PAGESPEED_NO_API_KEY"));
}
Json json = new Json(result);
Json pageStats = json.GetProperty("pageStats");
sb.AppendLine("<table class=\"librarytable\" style=\"width: 730px;\">");
sb.AppendFormat("<tr><th colspan=\"2\">{0}</th></tr>", json.GetTypedProperty("id"));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("TITLE"), json.GetTypedProperty("title"));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", "Score", json.GetProperty("ruleGroups").GetProperty("SPEED").GetTypedProperty("score"));
// page stats
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_TOTAL_RESOURCES"),
pageStats.GetTypedProperty("numberResources"));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_JS_RESOURCES"),
pageStats.GetTypedProperty("numberJsResources"));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_CSS_RESOURCES"),
pageStats.GetTypedProperty("numberCssResources"));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_STATIC_RESOURCES"),
pageStats.GetTypedProperty("numberStaticResources"));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_NUMBEROF_HOSTS"),
pageStats.GetTypedProperty("numberHosts"));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_TOTAL_BYTES"),
Helpers.BytesToString(Convert.ToInt64(pageStats.GetTypedProperty("totalRequestBytes"))));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_HTML_BYTES"),
Helpers.BytesToString(Convert.ToInt64(pageStats.GetTypedProperty("htmlResponseBytes"))));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_JS_BYTES"),
Helpers.BytesToString(Convert.ToInt64(pageStats.GetTypedProperty("javascriptResponseBytes"))));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_CSS_BYTES"),
Helpers.BytesToString(Convert.ToInt64(pageStats.GetTypedProperty("cssResponseBytes"))));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_IMG_BYTES"),
Helpers.BytesToString(Convert.ToInt64(pageStats.GetTypedProperty("imageResponseBytes"))));
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", Translations.GetString("PAGESPEED_OTHER_BYTES"),
Helpers.BytesToString(Convert.ToInt64(pageStats.GetTypedProperty("otherResponseBytes"))));
sb.AppendLine("</table>");
// ruleResults skipped
sb.AppendLine("</div>");
context.Response.Layout[ThreeColumnLayout.CenterColumn] = sb.ToString();
return ActionResult.ActionCompleted();
}
}
}
|
|
The subaction result will look like this:
