For our newsletter, I generate the final body of the email in a web page and then want to pull that into the body of the email. I found a way to do that with HttpWebRequest.
private string GetHtmlBody(Guid id)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(String.Format("http://news.domain.com/News/View/{0}", id.ToString()));
HttpWebResponse responce = (HttpWebResponse)request.GetResponse();
System.IO.StreamReader sr = new System.IO.StreamReader(responce.GetResponseStream());
return sr.ReadToEnd();
}
However, I feel there has to be a better way. Can I somehow pull the generated view without making a web call?
You could use MVCContrib for this task.
Or try to roll some ugly code:
public static string ViewToString(string controlName, object viewData)
{
var vd = new ViewDataDictionary(viewData);
var vp = new ViewPage { ViewData = vd };
var control = vp.LoadControl(controlName);
vp.Controls.Add(control);
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
using (HtmlTextWriter tw = new HtmlTextWriter(sw))
{
vp.RenderControl(tw);
}
return sb.ToString();
}
and then:
var viewModel = ...
string template = ViewToString("~/Emails/EmailTemplate.ascx", viewModel);
Assuming the the email code is in the same project as the website, then you should be able to call the action method, get the ActionResult back, then call the ExecuteResult method. The downside is that in order to do it this way, you will need to set it up such that the ExecuteResult will write to a stream that you can take advantage of. In order to do all of this, you will need to mock up some of the classes used by the ControllerContext.
What would probably be a better way (though will likely take more work), is to have the markup you want be generated by an XSLT transform. XSLT is a type of XML document template that can be merged with an XML document that holds data to produce a desired result. If you do this, then you can have your process that sends out emails run the transform as well as have your website run the transform. The advantage of this, is that if you want the markup to be different (i.e. you are redesigning thew newsletter), you will simply need to update the XSLT file and deploy it.
HTH
Finally got a working solution. After finding some proper search terms (thanks to #Darin) any many, many trials I found a solution that works. Putting this in my controller then passing the rendered string into my EmailHelper works great for what I needed.
Related
With the help of other Stackoverflow users, I have gone some way to my solution but have come to a halt.
I would like to build some generic classes in an app_code .cshtml file eg one would be to return property values from documents from a function eg
public static string docFieldValue(int docID,string strPropertyName){
var umbracoHelper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
var strValue = "";
try{
strValue = umbracoHelper.Content(docID).GetPropertyValue(strPropertyName).ToString();
}
catch(Exception ex){
strValue = "Error - invalid document field name (" + strPropertyName + ")";
}
var nContent = new HtmlString(strValue);
return nContent;
}
This works ok for returning one field (ie property) from a document. However, if I wanted to return 2 or more, ideally, I would store the returned node in a variable or class and then be able to fetch property values repeatedly without having to look up the document with each call
ie without calling
umbracoHelper.Content(docID).GetPropertyValue(strPropertyName).ToString();
with different strPropertyName parameters each time, as I assume that will mean multiple reads from the database).
I tried to build a class, with its properties to hold the returned node
using Umbraco.Web;
using Umbraco.Core.Models;
...
public static Umbraco.Web.UmbracoHelper umbracoHelper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
public static IPublishedContent docNode;
...
docNode = umbracoHelper.Content(docID);
but this crashed the code. Can I store the node in a property on a class, and if so, what type is it?
First of all, using a .cshtml file is unnecessary, use a .cs file instead :-) CSHTML files are for Razor code and HTML and stuff, CS files are for "pure" C#. That might also explain why your last idea crashes.
Second of all, UmbracoHelper uses Umbracos own cache, which means that the database is NOT touched with every request. I would at least define the umbracoHelper object outside of the method (so it gets reused every time the method is called instead of reinitialised).
Also, beware that property values can contain all kinds of other object types than strings.
EDIT
This is an example of the entire class file - my example namespace is Umbraco7 and my example class name is Helpers:
using Umbraco.Web;
namespace Umbraco7
{
public class Helpers
{
private static UmbracoHelper umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
private static dynamic docNode;
public static string docFieldValue(int docID, string strPropertyName)
{
docNode = umbracoHelper.Content(docID);
return docNode.GetPropertyValue(strPropertyName).ToString();
}
}
}
This is an example how the function is called inside a View (.cshtml file inside Views folder):
#Helpers.docFieldValue(1076, "introduction")
Helpers, again, is the class name I chose. It can be "anything" you want. I've just tested this and it works.
I suggest you read up on general ASP.NET MVC and Razor development, since this is not very Umbraco specific.
My final goal is to generate a go back button in my wicket site forms.
Right now I'm able to get the referrer with:
HttpServletRequest req = (HttpServletRequest)getRequest().getContainerRequest();
l.info("referer: {}", req.getHeader("referer"));
This works and I get the whole URL (as a String) but I'm unable to generate a Link object from this.
I'm not sure about the internals although I've been seeing the code for Application.addMount, IRequestHandler and more, I'm not able to find exactly where a URL is converted to what I need to generate a BookmarkablePageLink: the Class and the PageParameters.
P.S. I know this can be done with JavaScript, but I want to serve users without JS active.
Thanks
Possible solution I'm currently using:
public static WebMarkupContainer getBackButton(org.apache.wicket.request.Request request, String id) {
WebMarkupContainer l = new WebMarkupContainer(id);
HttpServletRequest req = (HttpServletRequest)request.getContainerRequest();
l.add(AttributeModifier.append("href", req.getHeader("referer")));
return l;
}
In my markup I have:
<a wicket:id="backButton">Back</a>
And then, in my Page object:
add(WicketUtils.getBackButton(getRequest(), "backButton");
If anyone has any better idea, I'm leaving this open for a while.
You should be able to use an ExternalLink for this.
Something resembling
public Component getBackButton(org.apache.wicket.request.Request request, String id) {
HttpServletRequest req = (HttpServletRequest)request.getContainerRequest();
String url = req.getHeader("referer");
return new ExternalLink(id, url, "Back");
}
with html
this body will be replaced
And your Page object code unchanged.
I need to get the component associated to a Activity at the event system.
I try to get the component ID using:
public void OnActivityInstanceFinishPost(ActivityInstance activityInstance, string finishMessage, string nextActivity, string dynamicAssignee)
{
if (activityInstance.ProcessInstance.ProcessDefinition.Title.Equals("Component Process IESE"))
{
if (activityInstance.ActivityDefinition.Title.Equals("Create or Edit Component"))
{
WFE workflow = tdse.GetWFE();
try
{
Component comp = (Component)activityInstance.ProcessInstance.Item;
XMLReadFilter filter = new XMLReadFilter();
String processHistoryId = activityInstance.ProcessInstance.ID.Replace("131076", "131080");
ProcessHistory hist = (ProcessHistory)tdse.GetObject(activityInstance.ProcessInstance.ID, EnumOpenMode.OpenModeView, Constants.URINULL, filter);
}
catch (Exception e)
{ }
}
}
}
we try different options:
Component comp = (Component)activityInstance.ProcessInstance.Item;
But this solution returns a null.
Then I found in internet the next solution:
XMLReadFilter filter = new XMLReadFilter();
String processHistoryId = activityInstance.ProcessInstance.ID.Replace("131076", "131080");
ProcessHistory hist = (ProcessHistory)tdse.GetObject(activityInstance.ProcessInstance.ID, EnumOpenMode.OpenModeView, Constants.URINULL, filter);
Component comp = hist.Item as Component;
But the ProcessHistory object is null.
How can I determine the component associated to the activityInstance?
Thank you.
After reviewing the functionality needed by Guskermitt, I've shown him a neater way to do what he needs to do. In short, EventSystem is not needed in this case.
His goal is to send an email after a component has been approved, the approach will be the following:
Add to workflow a new automatic activity.
Create a new .NET assembly, in this case a C# class to do what he needs to do.
Register the assembly in the GAC.
Add logic in the new automatic activity in workflow to use the .NET assembly.
2#
[ProgId("WfHelper")]
[ComVisible(true)]
public class Helper
{
public void SendMail(string workItemId)
{
var session = new Session();
.
.
.
4#
dim helper
set helper = CreateObject("WfHelper")
call helper.SendMail(CurrentWorkItem.ID)
set helper = nothing
FinishActivity “Email has been sent"
ActivityInstance has a WorkItems property (inherited from Activity) that contains a reference to your Component.
OnActivityInstanceFinishPost means that your activity is finished. Therefore there is no more work item associated with it. However, you are getting the process instance and the work item associated with that. If you get null there, then it suggests your workflow process is done and the component has moved out of workflow. From looking at your code, it is quite likely that your ProcessInstance is completed (it won't be null, but it won't have any item associated with it).
I suspect that you've read this post http://www.tridiondeveloper.com/autopublishing-on-workflow-finish suggesting to look in the history. Have you looked into the history via the CM GUI, is the history item there? If it isn't, that's why you get null. A workflow process gets moved to history when it is completed. So double check that you are indeed on the last workflow activity before looking at the history.
By looking at your code, the error seems to be that you are trying to get a history object using activityInstance.ProcessInstance.ID. GetObject() should return an item, but your cast to a ProcessHistory should break and then you quietly eat the exception. You need to pass in the History ID, not the ProcessInstance ID as follows:
ProcessHistory hist = (ProcessHistory)tdse.GetObject(processHistoryId, EnumOpenMode.OpenModeView, Constants.URINULL, filter);
I'm working with FB C# SDK. Probably my question is very basic but I have already tried tons of options and I can't solve my problem.
I'm using the get method to retrieve info from facebook, it works perfectly with albums, so it isn't a problem of access_token or initialization.
This is the line of code that I use and it returns me the object with the information of albums, for any user it will be always the same since the app asks for all his albums. It works fine:
dynamic fotos = client.Get("me/albums");
The problem comes when I ask for photos, because it's nos an static value like with albums, it's a value based on user selection, so, I use a variable instead:
public ActionResult Photos(string albumstring)
{
... more code asking for token, etc.
dynamic fotos = client.Get(albumstring);
There it fails and it returns me an empty object. The string text at albumstring is exactly the one needed: "10150240194251714/photos" but it just don't work.
I tested it staticly like this, and it works!:
public ActionResult Photos(string albumstring)
{
... more code asking for token, etc.
albumstring2 = "10150240194251714/photos";
dynamic fotos = client.Get(albumstring2);
But it only works if it's an static value, mixing it with a variable makes it fail, like in this example:
public ActionResult Photos(string albumId)
{
... more code asking for token, etc.
albumstring = albumId + "/photos";
dynamic fotos = client.Get(albumstring);
I have tried a lot of different ways to construct the string like StringBuilders, adding the escape char \, contructing it from the view and bringing it complete to the controller, etc. But it seems that if the string is not directly written it just won't work.
Probably it's an error from the fb c#sdk, but I need to workaround it and I'm out of ideas.
this is the method I use and it works fine to retrieve some album photosm where the class AlbumPhoto is just a DTO to capture de data. I hope it helps
public static List<AlbumPhoto> GetAlbumPhotos(string userAccessToken, string albumId)
{
var list = new List<AlbumPhoto>();
var client = new FacebookClient(userAccessToken);
dynamic photos = client.Get(String.Format("/{0}/photos", albumId));
foreach (var photo in photos.data)
{
lista.Add(new AlbumPhoto
{
FacebookId = photo.id,
Name = photo.name,
ImageUrl = photo.source,
Height = (int)photo.height,
Width = (int)photo.width
});
}
return list;
}
I presume this is possible? Basically i am receiving using LINQ (LINQ2Entities Entity Framework) data and return it to an IQUERYABLE once i this data i need to transform it to XML in a Memory Stream and a physical file on the hard disk - streamwriter??
Does anyone know if this is possible?
Any help really appreciated
Do i need to use LINQtoXML to accomplish this?
Any examples or tutorials anybody knows of would be great.
Thanks again
EDIT
After a little bit of investigation i think i require a XML Serializer for LINQ2Entities / IQueryable??
LINQ to XML is probably the best choice. You can use functional construction to create an XML tree in a single statement, like:
using (ContactEntities context = new ContactEntities()) {
XDocument xml = new XDocument(
new XElement("contacts",
from contact in context.Contacts
orderby contact.ContactId
select new XElement("contact",
new XAttribute("contactId", contact.ContactId),
new XElement("firstName", contact.FirstName),
new XElement("lastName", contact.LastName))));
xml.Save(yourStream);
}
Entity classes are by default Data Contracts, which means that they can be serialized based on the fields they contain:
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx
I know this question is a old, but I wanted to show my solution to this problem.
public static string CreateXml<T>(IQueryable<T> thisQueryable)
{
var thisList = thisQueryable.ToList();
var xmlResult = "";
using (var stringWriter = new StringWriter())
{
using (var xmlWriter = new XmlTextWriter(stringWriter))
{
var serializer = new XmlSerializer(typeof(List<T>));
serializer.Serialize(xmlWriter, thisList);
}
xmlResult = stringWriter.ToString();
}
return xmlResult;
}
Basically this just takes your IQueryable<T> and serializes it to XML and returns that XML as a string.
Then you'd basically take that string and....
var xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResult);
Hope this helps for any future visitors.